Programación funcional
Contents
Programación funcional#
Important
¿Cómo vamos?
Hasta el momento debemos tener claro lo siguiente:
La programación funcional es un paradigma de programación en el que el método principal de cálculo es la evaluación de funciones. La programación funcional no es tan usada en Python, sin embargo, hay que conocerlo porque seguramente habrán situaciones en la que esa forma de trabajar puede mejorar la eficiencia algorítmica.
En la programación funcional, cada programa consiste en la evaluación de funciones compuestas y el cálculo es libre de valores intermedios pues todo se contempla como entrada y salida de una función. En Python hay posibilidad de implementar este modelo de programación por dos razones:
Es posible tomar una función como argumento de otra y
es posible tener como salida una función.
En gran medida esto es por una razón muy particular de Python: ¡Todo es objeto!
Es decir, las funciones están al nivel de las cadenas de caracteres y los números. En el siguiente ejemplo, se define una función y se estudia como una variable comun y corriente:
def func(x):
return x*5
otrafun=func
otrafun(20)
100
Observa que:
id(func)
140139792738336
id(otrafun)
140139792738336
Acabamos de ver que tiene un comportamiento de variable, hagamos un ejemplo más atrevido, incluiremosla función en una lista y la evaluaremos con la lista:
Lista=[1,2,func]
print(Lista)
[1, 2, <function func at 0x7f74d690a820>]
Lista[2](10)
50
La flexibilidad de la función permite que se evalúe una función como argumento de otra, veamos un ejemplo:
def func(x):
return x*5
def externa(funcion,x):
return funcion(x+52)
externa(func,10)
310
Note
Python permite usar decoradores para facilitar la sintáxis de esta composición de funciones, por ejemplo:
def decorador(x):
def wrapper(funcion):
print("Something is happening before the function is called.")
funcion(x+52)
print("Something is happening after the function is called.")
return wrapper
@decorador(10)
def func(x):
print(x*5)
Something is happening before the function is called.
310
Something is happening after the function is called.
Veámoslo con algo más sencillo:
def my_decorator(func):
def wrapper():
print("Vamos a llamar a la función que dice !Viva la vida!")
func()
print("Ya lo dijo.")
return wrapper
@my_decorator
def vivalavida():
print("¡Viva la vida!")
vivalavida()
Vamos a llamar a la función que dice !Viva la vida!
¡Viva la vida!
Ya lo dijo.
Los diferentes niveles de llamada de la función son importantes en la definición, tenemos por ejemplo:
def funcion_1():
def funcion_2():
print('Hola, estoy metido en dos funciones')
return funcion_2
otrafun=funcion_1()
otrafun()
Hola, estoy metido en dos funciones
funcion_1()()
Hola, estoy metido en dos funciones
Como se puede apreciar, para poder obtener el resultado tuvimos que poner dos paréntesis en la funcion_1.
Definición de funciones con lambda#
En Python podemos generar funciones rápidamente y de manera anónima, se trata de las expresiones lambda, una forma corta y concisa de declarar funciones en Python. Su sintáxis es la siguiente:
lambda argumentos: expresión
(lambda x: x+4)(2)
6
Podriamos guardar la expresión lambda en una variable:
cuadrado= lambda x: x**2
cuadrado(10)
100
Aprovechemos y miremos el tipo de dato que guarda una expresión lambda:
type(cuadrado)
function
observe que pasa con la función definida de manera habitual:
def cuadrado(x):
return x**2
type(cuadrado)
function
La función lambda admite varios argumentos, por ejemplo:
suma=lambda x,y: x+y
suma(3,5)
8
Pero solo se deja una salida:
suma_y_resta=lambda x,y: x+y,x-y
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Input In [23], in <cell line: 1>()
----> 1 suma_y_resta=lambda x,y: x+y,x-y
NameError: name 'x' is not defined
El error es ocasionado porque no sale un solo elemento, podemos usar tuplas o listas para obtener varios datos:
suma_y_resta=lambda x,y: (x+y,x-y)
suma_y_resta(8,3)
(11, 5)
Funciones especiales para aplicar funciones#
Para una adecuada implementación de un estilo funcional de programación, Python cuenta con tres métodos muy especiales que complementan muy bien este paradigma. Hablamos de map()
y filter()
. Veamos en detalle algunos ejemplos que nos pueden orientar en su practicidad:
map()
#
La función map()
es una función integrada de Python que aplica una función a cada elemento de un iterable. En el estiulo funcional se asemeja a una estructura de iteración y por lo tanto podría verse como un bucle declarado (vale la pena recordar esos dos tipos de programación: declarada e imperativa).
La sintaxis de map es:
map(funcion,iterable)
map(lambda x: x+'2','hola')
<map at 0x7f16bd69ad90>
El objeto que map
guarda es un iterador, ya conocemos varias formas de verlo:
ejemplo=map(lambda x: x+'2','hola')
for i in ejemplo:
print(i)
h2
o2
l2
a2
ejemplo=map(lambda x: x+'2','hola')
list(ejemplo)
['h2', 'o2', 'l2', 'a2']
ejemplo=map(lambda x: x+'2','hola')
set(ejemplo) ## No tan bueno
{'a2', 'h2', 'l2', 'o2'}
Otro ejemplo bien interesante puede ser:
ejemplo2=map(lambda x: True if x**2 >= 5050 else False,range(100))
Recuerden usar list
para ver el resultado:
list(ejemplo2)
map con varios iterables
map()
admite varios iterables en sus argumentos, tenemos que:
map(funcion, iterable_1,iterable_2,...,iterable_n)
En este caso, la función admite \(n\) variables y la longitud de la salida coincide con la de cada iterable. De hecho, usando notación matemática tenemos:
list(map(lambda x,y,z: x+y+z+'2','hola','adios','chao'))
['hac2', 'odh2', 'lia2', 'aoo2']
Todos los iterables deben ser de la misma longitud, si no tendremos error:
list(map(lambda x,y,z: x+y+z+'2','hola','adios','chao','bye'))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/tmp/ipykernel_41554/1713898509.py in <module>
----> 1 list(map(lambda x,y,z: x+y+z+'2','hola','adios','chao','bye'))
TypeError: <lambda>() takes 3 positional arguments but 4 were given
Otro ejemplo más aclarador:
list(map(lambda x,y,z: x+y+z,'ABCDE','abcde','12345'))
['Aa1', 'Bb2', 'Cc3', 'Dd4', 'Ee5']
filter()
#
La función filter()
permite seleccionar elementos de un iterable según la función dada, por ejemplo:
filter(lambda x:x if x!='a' else '','armadura')
<filter at 0x7f16bda43790>
Lo convertimos en lista:
list(filter(lambda x:x if x!='a' else '','armadura'))
['r', 'm', 'd', 'u', 'r']
Por ejemplo podemos extraer los pares de la lista de los primeros 30 enteros positivos:
def es_par(x):
return x%2==0
list(filter(es_par,range(1,31)))
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]
Es una función muy util, combina el poder de un bucle con un condicional. Resulta bastante útil para diferentes probemas de selección.
Cierre#
En esta sección exploramos conceptos elementales de la programación funcional, es evidente su diferencia con la programación estructurada que conociamos y posiblemente habrá muchos algoritmos que para nuestra formación inicial no se puedan llevar a cabo con este estilo de programación. No obstante, la potencia de poder aplicar funciones y de crear algoritmos en este sentido se utiliza bastante en el análisis de tablas de datos, de hecho, siguiendo los lineamientos de este estilo es más fácil paralelizar la computación y el valor final de las ejecuciones es más transparente que lo que se obtiene por la programación estructurada.