Elementos de Programación Funcional en Python

Introducción

Python como varios otros lenguajes modernos como ser ruby, scala, etc., tiene influencia del paradigma funcional. No vamos acá a enseñar el paradigma funcional, pero sí sucede que varias de estas ideas están relacionadas con la manipulación de estructuras de datos contenedoras, como las listas, diccionarios, sets, etc.

Entonces en esta sección vamos a ver algunas ideas que, luego de entenderlas aplicadas sobre las colecciones, podremos utilizarlas luego para modelar cualquier problema. Y tendremos un conjunto de "herramientas" para pensar soluciones a problemas.

En general estas ideas llevan a combinar el paradigma estructurado y objetos con la simplicidad y legibilidad del paradigma funcional y declarativo.

Funciones de orden superior (higher-order functions)

En general las funciones que veníamos viendo hasta el momento se denominan funciones de primer nivel. Porque existe la idea de funciones de orden superior. Que se refiere a funciones que:

    • Reciben otra función como uno o varios parámetro/s.
    • O bien retornan otra función como resultado.

Y claro, se llaman de orden superior porque operan sobre funciones. El dominio o la imagen de estas funciones son funciones.

Veamos un ejemplo. Empezamos por uno simple, para no decir bastante "pavo", porque no es justamente para lo que uno usaría estas funciones realmente.

Supongamos que tenemos una función que sirve para "saludar". Símplemente hace un print.

def saludar():
    print 'Hola!'

Ahora supongamos que en nuestro programa necesitamos saludar 5 veces. Deberíamos tener en algún lado:

saludar()
saludar()
saludar()
saludar()
saludar()

O si no queremos repetir o queremos hacerlo dinámico, tendríamos un for..

Ahora, resulta que nos damos cuenta que en realidad esto es algo así como un patrón, sucede en nuestra aplicación que ciertos comportamientos los tenemos que ejecutar varias veces. No solo el saludar, si no otras operaciones. Que en nuestro caso operaciones = funciones.

Entonces, podríamos pensar en hacer una función que sirva para ejecutar otra función tantas veces como le digamos:

def saludar():
    print 'Hola!'
   
def nVeces(unaFuncion, n):
    for _ in range(n):
        unaFuncion()
       
nVeces(saludar, 5)

nVeces es una función de orden superior. Porque recibe como parámetro otra función (además de un número que especifica cuántas veces ejecutarla).

Ahí vemos que no hay nada mágico en la función.

La única particularidad es que:

    • las funciones se pasan como parámetro especificando su nombre sin los paréntesis (de otra forma la estaríamos ejecutando)
    • la declaración del parámetro "unaFuncion" no tiene nada especial.
    • La función superior puede dentro del método invocar la función por parámetro con su nombre y los paréntesis.

Así ahora podemos llamarla con cualquier otra función, como irse:

def irse():
    print 'Chau!'
   
nVeces(saludar, 5)
nVeces(irse, 3)

Quizás no se vea ahora el impacto de esta idea, pero créannos que es uno de las ideas más representativas del paradigma funcional y por lo tanto cambia la forma de resolver y pensar problemas.

Porque provee:

    • Una forma de encontrar nuevas abstracciones (funciones superiores) que a su vez...
    • permiten reutilizar código
    • separar la lógica por niveles y así tener funciones "incompletas" en cuanto a que les falta una parte de su comportamiento que se completará con parámetros. Es decir que estamos parametrizando las funciones.

En las siguientes secciones vamos a ver ejemplos más representativos con colecciones.

Para más información sobre ésto ver ésta página del wiki de Uqbar