93 - Funciones lambda en Python

Una función lambda es cuando enviamos a una función de orden superior directamente una función anónima.

Es más común enviar una expresión lambda en lugar de enviar la referencia de una función como vimos en el concepto anterior.

Problema 1

Definir una función de orden superior llamada operar. Llegan como parámetro dos enteros y una función. En el bloque de la función llamar a la función que llega como parámetro y enviar los dos primeros parámetros.

Desde el bloque principal llamar a operar y enviar distintas expresiones lambdas que permitan sumar, restar, multiplicar y dividir .

Programa: ejercicio322.py

Ver video

def operar(v1,v2,fn):
    return fn(v1,v2)

resu1=operar(10,3,lambda x1,x2: x1+x2)
print(resu1)

resu2=operar(10,3,lambda x1,x2: x1-x2)
print(resu2)

print(operar(30,10,lambda x1,x2: x1*x2))

print(operar(10,2,lambda x1,x2: x1/x2))

La función de orden superior operar es la que vimos en el concepto anterior:

def operar(v1,v2,fn):
    return fn(v1,v2)

La primer expresión lambda podemos identificarla en la primer llamada a la función operar:

resu1=operar(10,3,lambda x1,x2: x1+x2)
print(resu1)

Una expresión lambda comienza por la palabra clave 'lambda' y está compuesta por una serie de parámetros (en este caso x1 y x2), el signo ':' y el cuerpo de la función:

x1+x2

El nombre de los parámetros se llaman x1 y x2, pero podrían tener cualquier nombre:

resu1=operar(10,3,lambda valor1,valor2: valor1+valor2)

Se infiere que la suma de x1 y x2 es el valor a retornar.

La segunda llamada a operar le pasamos una expresión lambda similar a la primer llamada:

resu2=operar(10,3,lambda x1,x2: x1-x2)
print(resu2)

Las otras dos llamadas son iguales:

print(operar(30,10,lambda x1,x2: x1*x2))

print(operar(10,2,lambda x1,x2: x1/x2))

Problema 2

Confeccionar una función de orden superior que reciba una lista que almacena valores enteros y una función con un parámetro entero y que retorne un boolean.

La función debe analizar cada elemento de la lista llamando a la función que recibe como parámetro, si retorna un True se pasa a mostrar el elemento.

En el bloque principal definir una lista de enteros.

Imprimir de la lista:

  • Los valores múltiplos de 2
  • Los valores múltiplos de 3 o de 5
  • Los valores mayores o iguales a 50
  • Los valores comprendidos entre 1 y 50 o entre 70 y 100.

Programa: ejercicio323.py

Ver video

def imprimir_si(lista, fn):
    for elemento in lista:
        if fn(elemento):
            print(elemento)


lista1=[9, 20, 70, 60, 19]
print("Valores pares de la lista")
imprimir_si(lista1, lambda x: x%2==0)
print("Valores múltiplos de 3 o de 5")
imprimir_si(lista1, lambda x: x%3==0 or x%5==0)
print("Imprimir valores mayores o iguales a 50")
imprimir_si(lista1, lambda x: x>=50)
print("Imprimir los valores comprendidos entre 1 y 50 o 70 y 100")
imprimir_si(lista1, lambda x: x>=1 and x<=50 or x>=70 and x<=100)
print("Imprimir la lista completa")
imprimir_si(lista1, lambda x: True )

La función de orden superior 'imprimir_si' recibe una lista y una función:

def imprimir_si(lista, fn):

Dentro de la función recorremos los elementos de la lista y llamamos a la función que recibe como parámetro para cada elemento de la lista, si retorna un True pasamos a mostrar el valor:

    for elemento in lista:
        if fn(elemento):
            print(elemento)

La ventaja de la función 'imprimir_si' es que podemos utilizarla para resolver una gran cantidad de problemas, solo debemos pasar una expresión lambda que analice cada entero que recibe.

En el bloque principal creamos la lista y cargamos 5 valores:

lista1=[9, 20, 70, 60, 19]

Para imprimir los valores múltiplos de 2 nuestra expresión lambda recibe un parámetro llamado 'x' y el algoritmo que verifica si es par o no consiste en verificar si el resto de dividirlo por 2 es cero:

print("Valores múltiplos de 2")
imprimir_si(lista1, lambda x: x%2==0)

Es importante entender que nuestra expresión lambda no tiene una estructura repetitiva, sino que desde la función 'imprimir_si' llamará a esta función anónima tantas veces como elementos tenga la lista.

El algoritmo de la función varía si queremos identificar si el número es múltiplo de 3 o de 5:

print("Valores múltiplos de 3 o de 5")
imprimir_si(lista1, lambda x: x%3==0 or x%5==0)

Para imprimir todos los valores mayores o iguales a 50 debemos verificar si el parámetro x es >= 50:

print("Imprimir valores mayores o iguales a 50")
imprimir_si(lista1, lambda x: x>=50)

Para analizar si la componente se encuentra en distintos rangos:

print("Imprimir los valores comprendidos entre 1 y 50 o 70 y 100")
imprimir_si(lista1, lambda x: x>=1 and x<=50 or x>=70 and x<=100)

Previo al if indicamos el valor a retornar si la condición del if se verifica verdadero y en el caso contrario por el else indicamos el otro valor.

En forma muy sencilla si queremos imprimir todo el arreglo retornamos para cada elemento que analiza nuestra expresión lambda el valor True:

print("Imprimir la lista completa")
imprimir_si(lista1, lambda x: True )

Este es un problema donde ya podemos notar las potencialidades de las funciones de orden superior y las expresiones lambdas que le pasamos a las mismas.

Problema 3

Confeccionar una función de orden superior que reciba un String y una función con un parámetro de tipo String que retorna un Boolean.

La función debe analizar cada elemento del String llamando a la función que recibe como parámetro, si retorna un True se agrega dicho caracter al String que se retornará.

En el bloque principal definir un String con una cadena cualquiera.

Llamar a la función de orden superior y pasar expresiones lambdas para filtrar y generar otro String con las siguientes restricciones:

  • Un String solo con las vocales
  • Un String solo con los caracteres en minúsculas
  • Un String con todos los caracteres no alfabéticos

Programa: ejercicio324.py

Ver video

def filtrar(cadena,fn):
    cad=""
    for caracter in cadena:
        if fn(caracter):
            cad=cad+caracter
    return cad


cadena="¿Esto es la prueba 1 o la prueba 2?"
print("String original")
print(cadena)
print("String solo con las vocales")
cadena2=filtrar(cadena, lambda car: car == 'a' or car == 'e' or car == 'i' or car == 'o' or car == 'u' 
                                    or car == 'A' or car == 'E' or car == 'I' or car == 'O' or car == 'U')
print(cadena2)

print("String solo con los caracteres en minúscula")
cadena3=filtrar(cadena, lambda car: car >='a' and car <= 'z')
print(cadena3)

print("String solo con los caracteres no alfabéticos")
cadena3=filtrar(cadena, lambda car: not(car >='a' and car <= 'z') and not(car >='A' and car <= 'Z'))
print(cadena3)

Nuestra función de orden superior se llama filtrar y recibe un String y una función:

def filtrar(cadena,fn):

El algoritmo de la función filtrar consiste en recorrer el String que llega como parámetro y llamar a la función fn que es la que informará si el caracter analizado debe formar parte del String final a retornar.

    cad=""
    for caracter in cadena:
        if fn(caracter):
            cad=cad+caracter
    return cad

En el bloque principal definimos un String con una cadena cualquiera:

cadena="¿Esto es la prueba 1 o la prueba 2?"
print("String original")
print(cadena)

La primer llamada a la función de orden superior la hacemos enviando una expresión lambda que considere parte del String a generar solo las vocales minúsculas y mayúsculas:

print("String solo con las vocales")
cadena2=filtrar(cadena, lambda car: car == 'a' or car == 'e' or car == 'i' or car == 'o' or car == 'u' 
                                    or car == 'A' or car == 'E' or car == 'I' or car == 'O' or car == 'U')
print(cadena2)

Para generar un String solo con las letras minúsculas debemos verificar si el parámetro de la función anónima se encuentra en el rango 'a' y 'z':

print("String solo con los caracteres en minúscula")
cadena3=filtrar(cadena, lambda car: car >='a' and car <= 'z')
print(cadena3)

Por último para recuperar todos los símbolos que no sean letras expresamos la siguiente condición:

print("String solo con los caracteres no alfabéticos")
cadena3=filtrar(cadena, lambda car: not(car >='a' and car <= 'z') and not(car >='A' and car <= 'Z'))
print(cadena3)

Con este problema podemos seguir apreciando las grandes ventajas que nos proveen las expresiones lambdas para la resolución de algoritmos.