Funciones map, filter y reduce
Introducción
En la programación funcional, es común necesitar transformar, filtrar o combinar los elementos de una colección de datos. Python proporciona tres funciones fundamentales para estas operaciones: map()
, filter()
y reduce()
. Estas funciones nos permiten trabajar con colecciones de datos de manera más declarativa y expresiva, centrándonos en qué queremos hacer y no tanto en cómo hacerlo paso a paso.
En este artículo, aprenderemos cómo utilizar estas tres poderosas herramientas y cómo pueden mejorar la eficiencia y legibilidad de nuestro código Python. Veremos ejemplos prácticos que ilustran su uso y compararemos estos enfoques funcionales con las alternativas imperativas tradicionales.
La función map()
La función map()
aplica una función a cada elemento de un iterable (como una lista, tupla o cadena) y devuelve un objeto map, que puede ser convertido a una lista u otro iterable.
Sintaxis básica
map(funcion, iterable)
funcion
: La función que se aplicará a cada elementoiterable
: Colección de elementos a procesar
Ejemplos prácticos
Ejemplo 1: Convertir cadenas a números
# Convertir una lista de cadenas a números enteros
cadenas = ["1", "2", "3", "4", "5"]
numeros = list(map(int, cadenas))
print(numeros) # [1, 2, 3, 4, 5]
Ejemplo 2: Calcular el cuadrado de cada número
# Usando una función definida
def calcular_cuadrado(x):
return x ** 2
numeros = [1, 2, 3, 4, 5]
cuadrados = list(map(calcular_cuadrado, numeros))
print(cuadrados) # [1, 4, 9, 16, 25]
# O usando una función lambda (anónima)
cuadrados_lambda = list(map(lambda x: x ** 2, numeros))
print(cuadrados_lambda) # [1, 4, 9, 16, 25]
Ejemplo 3: Trabajar con múltiples iterables
map()
puede trabajar con múltiples iterables. En este caso, la función debe aceptar tantos argumentos como iterables se proporcionen:
# Sumar elementos de dos listas
lista1 = [1, 2, 3]
lista2 = [10, 20, 30]
suma = list(map(lambda x, y: x + y, lista1, lista2))
print(suma) # [11, 22, 33]
Comparación con enfoque imperativo
Comparemos el uso de map()
con un enfoque tradicional usando bucles:
# Enfoque funcional con map()
numeros = [1, 2, 3, 4, 5]
cuadrados_map = list(map(lambda x: x ** 2, numeros))
# Enfoque imperativo con bucle for
cuadrados_for = []
for num in numeros:
cuadrados_for.append(num ** 2)
print(cuadrados_map) # [1, 4, 9, 16, 25]
print(cuadrados_for) # [1, 4, 9, 16, 25]
El enfoque con map()
es más conciso y expresa directamente nuestra intención: aplicar una operación a cada elemento.
La función filter()
La función filter()
crea un iterador con los elementos de un iterable para los cuales una función devuelve True.
Sintaxis básica
filter(funcion, iterable)
funcion
: Función que devuelve True o False para cada elementoiterable
: Colección de elementos a filtrar
Ejemplos prácticos
Ejemplo 1: Filtrar números pares
# Filtrar números pares
numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
pares = list(filter(lambda x: x % 2 == 0, numeros))
print(pares) # [2, 4, 6, 8, 10]
Ejemplo 2: Filtrar cadenas vacías
# Eliminar cadenas vacías de una lista
cadenas = ["hola", "", "Python", "", "programación", "funcional", ""]
no_vacias = list(filter(lambda x: x != "", cadenas))
print(no_vacias) # ['hola', 'Python', 'programación', 'funcional']
# Alternativa más pythónica
no_vacias_alt = list(filter(bool, cadenas))
print(no_vacias_alt) # ['hola', 'Python', 'programación', 'funcional']
Ejemplo 3: Filtrar elementos según una condición compleja
# Filtrar estudiantes aprobados (nota >= 5)
estudiantes = [
{"nombre": "Ana", "nota": 8.5},
{"nombre": "Luis", "nota": 4.7},
{"nombre": "Carmen", "nota": 9.2},
{"nombre": "Pedro", "nota": 3.8},
{"nombre": "Sofia", "nota": 7.1}
]
aprobados = list(filter(lambda estudiante: estudiante["nota"] >= 5, estudiantes))
for estudiante in aprobados:
print(f"{estudiante['nombre']}: {estudiante['nota']}")
# Ana: 8.5
# Carmen: 9.2
# Sofia: 7.1
Comparación con enfoque imperativo
# Enfoque funcional con filter()
numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
pares_filter = list(filter(lambda x: x % 2 == 0, numeros))
# Enfoque imperativo con bucle for
pares_for = []
for num in numeros:
if num % 2 == 0:
pares_for.append(num)
print(pares_filter) # [2, 4, 6, 8, 10]
print(pares_for) # [2, 4, 6, 8, 10]
El enfoque con filter()
comunica más claramente nuestra intención de seleccionar elementos que cumplen cierta condición.
La función reduce()
La función reduce()
aplica cumulativamente una función a los elementos de un iterable, de izquierda a derecha, para reducirlo a un único valor. A diferencia de map()
y filter()
, reduce()
no está disponible directamente y debe importarse del módulo functools
.
Sintaxis básica
from functools import reduce
reduce(funcion, iterable[, inicial])
funcion
: Función que toma dos argumentos (acumulador y elemento actual)iterable
: Colección de elementos a reducirinicial
: Valor inicial del acumulador (opcional)
Ejemplos prácticos
Ejemplo 1: Suma de todos los elementos
from functools import reduce
# Sumar todos los elementos de una lista
numeros = [1, 2, 3, 4, 5]
suma = reduce(lambda acum, valor: acum + valor, numeros)
print(suma) # 15
# Con valor inicial
suma_con_inicio = reduce(lambda acum, valor: acum + valor, numeros, 10)
print(suma_con_inicio) # 25 (10 + 1 + 2 + 3 + 4 + 5)
Ejemplo 2: Calcular el máximo valor
from functools import reduce
# Encontrar el valor máximo
numeros = [23, 12, 45, 67, 9, 34]
maximo = reduce(lambda x, y: x if x > y else y, numeros)
print(maximo) # 67
# Equivalente a usar la función max()
print(max(numeros)) # 67
Ejemplo 3: Composición de funciones
from functools import reduce
# Composición de funciones
def componer(*funciones):
def composed_function(x):
return reduce(lambda valor, funcion: funcion(valor), funciones, x)
return composed_function
# Funciones individuales
def duplicar(x):
return x * 2
def incrementar(x):
return x + 1
def cuadrado(x):
return x ** 2
# Crear una composición: f(x) = cuadrado(incrementar(duplicar(x)))
f = componer(duplicar, incrementar, cuadrado)
print(f(5)) # 121 (((5*2)+1)^2)
Cómo funciona reduce()
Para entender mejor cómo funciona reduce()
, veamos paso a paso su ejecución en el primer ejemplo:
reduce(lambda acum, valor: acum + valor, [1, 2, 3, 4, 5])
Pasos:
1. acum = 1, valor = 2 → acum = 3
2. acum = 3, valor = 3 → acum = 6
3. acum = 6, valor = 4 → acum = 10
4. acum = 10, valor = 5 → acum = 15
Resultado: 15
Si proporcionamos un valor inicial:
reduce(lambda acum, valor: acum + valor, [1, 2, 3, 4, 5], 10)
Pasos:
1. acum = 10, valor = 1 → acum = 11
2. acum = 11, valor = 2 → acum = 13
3. acum = 13, valor = 3 → acum = 16
4. acum = 16, valor = 4 → acum = 20
5. acum = 20, valor = 5 → acum = 25
Resultado: 25
Comparación con enfoque imperativo
from functools import reduce
# Enfoque funcional con reduce()
numeros = [1, 2, 3, 4, 5]
producto_reduce = reduce(lambda x, y: x * y, numeros)
# Enfoque imperativo con bucle for
producto_for = 1
for num in numeros:
producto_for *= num
print(producto_reduce) # 120 (1*2*3*4*5)
print(producto_for) # 120 (1*2*3*4*5)
Combinando map, filter y reduce
Estas funciones pueden combinarse para crear operaciones de procesamiento de datos más complejas:
from functools import reduce
# Lista de precios de productos
precios = [29.99, 10.50, 55.00, 7.95, 15.75, 49.99, 12.25]
# Calcular el total de los productos con precio > 20, con 10% de descuento
total = reduce(
lambda acum, precio: acum + precio,
map(
lambda precio: precio * 0.9, # Aplicar 10% de descuento
filter(
lambda precio: precio > 20, # Filtrar precios > 20
precios
)
)
)
print(f"Total con descuento: {total:.2f} €") # Total con descuento: 121.78 €
Este ejemplo muestra cómo:
- Primero filtramos los precios mayores de 20 con
filter()
- Luego aplicamos un 10% de descuento a cada precio con
map()
- Finalmente, sumamos todos los precios con
reduce()
Alternativas a map, filter y reduce
Python ofrece otras formas de implementar estas operaciones, a menudo más legibles:
Comprensión de listas
numeros = [1, 2, 3, 4, 5]
# map con comprensión de listas
cuadrados = [x**2 for x in numeros]
# filter con comprensión de listas
pares = [x for x in numeros if x % 2 == 0]
print(cuadrados) # [1, 4, 9, 16, 25]
print(pares) # [2, 4]
Funciones integradas
numeros = [1, 2, 3, 4, 5]
# reduce para suma → función sum()
suma = sum(numeros)
# reduce para producto
from functools import reduce
producto = reduce(lambda x, y: x * y, numeros)
# No hay equivalente directo en funciones integradas
print(suma) # 15
print(producto) # 120
Resumen
En este artículo hemos explorado las funciones map()
, filter()
y reduce()
, herramientas fundamentales de la programación funcional en Python. Estas funciones nos permiten transformar, filtrar y combinar datos de manera concisa y expresiva, lo que resulta en código más limpio y mantenible.
Aunque Python ofrece alternativas como las comprensiones de listas que pueden ser más legibles en muchos casos, comprender estas funciones funcionales nos proporciona mayor flexibilidad y nos ayuda a pensar en términos de transformaciones de datos. En el próximo artículo, exploraremos los decoradores, otra poderosa característica de Python relacionada con la programación funcional.