Apéndice P — Funciones lambda.

Objetivo. Describir el funcionamiento de la expresión lambda.

P.1 Programación funcional.

  • Es un paradigma de programación basado en el uso de funciones, entendiendo el concepto de función según su definición matemática.

  • Tiene sus raíces en el cálculo lambda (un sistema formal desarrollado en los años 1930 para investigar la definición de función, la aplicación de las funciones y la recursión).

  • Muchos lenguajes de programación funcionales pueden ser vistos como elaboraciones del cálculo lambda.

  • Las funciones que se usan en este paradigma son funciones puras, es decir, que no tienen efectos secundarios, que no manejan datos mutables o de estado.

  • Lo anterior está en contraposición con la programación imperativa.

  • Uno de sus principales representantes es el lenguaje Haskell, que compite en belleza, elegancia y expresividad con Python.

  • Los programas escritos en un estilo funcional son más fáciles de probar y depurar.

  • Por su característica modular facilita el cómputo concurrente y paralelo.

  • El estilo funcional se lleva muy bien con los datos, permitiendo crear algoritmos y programas más expresivos para trabajar en Big Data.

P.2 Lambda expressions

  • Una expresión Lambda (Lambda expressions) nos permite crear una función “anónima” (sin nombre). De esta manera es posible crear funciones ad-hoc sin la necesidad de definir una función propiamente con el comando def.

  • Una expresión Lambda o función anónima, es una expresión simple, no un bloque de declaraciones.

  • Solo hay que escribir el resultado de una expresión en vez de regresar un valor explícitamente.

  • Dado que se limita a una expresión, una función anónima es menos general que una función normal creada con def.

Por ejemplo, para calcular el cuadrado de un número podemos escribir la siguiente función:

def square1(n):
    """
    Calcula el cuadrado de n y lo regresa. Versión 1.0
    """
    result = n**2
    return result
print(square1(5))
25

Se puede reducir el código anterior como sigue:

def square2(n):
    """
    Calcula el cuadrado de n y lo regresa. Versión 2.0
    """
    return n**2
print(square2(5))
25

Se puede reducir aún más, pero puede llevarnos a un mal estilo de programación. Por ejemplo:

def square3(n): return n**2
print(square3(5))
25

P.2.1 Definición.

La sintáxis de una expresión lambda en Python (función lambda o función anónima) es muy simple:

lambda argument_list: expression
  1. La lista de argumentos, argument_list, consiste de objetos separados por coma.
  2. La expresión, expression, es cualquiera que sea válida en Python.

Se puede asignar la función a una variable para darle un nombre.

Ejemplo P.1: Un número al cuadrado.

Función anónima para el cálculo del cuadrado de un número.

# Se crea una función anónima
lambda n: n**2
<function __main__.<lambda>(n)>

Para poder usar la función anterior debe estar en un contexto donde pueda ser ejecutada o podemos darle un nombre como sigue:

# La función anónima se llama ahora cuadrado()
cuadrado = lambda num: num**2
# Usamos la función cuadrado()
print(cuadrado(7))
49

.

Ejemplo P.2: Un número al cubo.

Escribir una función lambda para calcular el cubo de un número usando la función lambda que calcula el cuadrado.

# Construimos la función cubo() usando la función cuadrado()
cubo = lambda n: cuadrado(n) * n
cubo(5)
125

.

Ejemplo P.3: Un número a una potencia.

Construir una función que genere funciones para elevar un número a a una potencia n.

Este ejemplo nos permite mostrar que es posible combinar la definición de funciones normales de Python con las funciones lambda.

def potencia(n):
    return lambda a: a ** n # regresa una función lambda
# Creamos dos funciones.
cuadrado = potencia(2) # función para elevar al cuadrado
cubo = potencia(3) # función para elevar al cubo
print(cuadrado(5))
print(cubo(2))
25
8

.

Ejemplo P.4: Multiplicar dos números.

Escribir una función lambda para multiplicar dos números.

En este ejemplo vemos como una función lambda puede recibir más de un argumento.

mult = lambda a, b: a * b
print(mult(5,3))
15

.

Ejemplo P.5: Números pares.

Checar si un número es par.

En este ejemplo usamos el operador ternario para probar una condición.

esPar = lambda n: False if n % 2 else True
print(esPar(2))
print(esPar(3))
True
False

.

Ejemplo P.6: Primer y último elemento.

Obtener el primer y último elemento de una secuencia, la cual puede ser una cadena, una lista o una tupla.

firstLast= lambda s: (s[0], s[-1])
# Cadena
firstLast('Pythonico')
('P', 'o')
# Lista
firstLast([1,2,3,4,5,6,7,8,9])
(1, 9)
# Tupla
firstLast( (1.2, 3.4, 5.6, 8.4) )
(1.2, 8.4)

.

Ejemplo P.7: Una secuencia en reversa.

Escribir en reversa una secuencia que puede ser una cadena, una lista o una tupla.

c = 'Pythonico'

reversa = lambda l: l[::-1]

print(c)
print(reversa(c))
Pythonico
ocinohtyP

P.3 Funciones puras e impuras

  • La programación funcional busca usar funciones puras, es decir, que no tienen efectos secundarios, no manejan datos mutables o de estado.

  • Estas funciones puras devuelven un valor que depende solo de sus argumentos.

Por ejemplo, podemos construir funciones que hagan un cálculo aritmético el cuál solo depende de sus entradas y no modifica otra cosa:

# La siguiente es una función pura
def operacion1(x, y):
    return (x + 2 * y) / (2 * x + y)

operacion1(1,2)
1.25
# La siguiente es una función lambda pura
operacion2 = lambda x, y: (x + 2 * y) / (2 * x + y)

operacion2(1,2)
1.25

El que sigue es un ejemplo de una función impura que tiene efectos colaterales en la lista:

lista = [] # lista vacía

# Esta es una función impura
def operacion3(arg):
    potencia = 2
    lista.append(arg) # Se modifica la lista
    return arg ** potencia

operacion3(5)

print(lista)
[5]

Lo anterior también puede suceder usando funciones lambda:

# Función lambda impura:
operacion4 = lambda l, arg : (l.append(arg), arg**2)
print(operacion4(lista,5))
lista
(None, 25)
[5, 5]

Una práctica recomendable del estilo funcional es evitar los efectos secundarios, es decir, que nuestras funciones, en la medida de lo posible, NO modifiquen los valores de sus argumentos.