{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Programación Funcional con Python"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"*Esta notebook fue creada originalmente como un blog post por [Raúl E. López Briega](http://relopezbriega.com.ar/) en [Mi blog sobre Python](http://relopezbriega.github.io). El contenido esta bajo la licencia BSD.*"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Introducción\n",
"\n",
"Es bien sabido que existen muchas formas de resolver un mismo problema, esto, llevado al mundo de la programación, a generado que existan o co-existan diferentes *[estilos](http://es.wikipedia.org/wiki/Estilo_de_programaci%C3%B3n)* en los que podemos programar, los cuales son llamados generalmente *[paradigmas](http://es.wikipedia.org/wiki/Paradigma_de_programaci%C3%B3n)*. Así, podemos encontrar basicamente 4 *[paradigmas](http://es.wikipedia.org/wiki/Paradigma_de_programaci%C3%B3n)* principales de programación:\n",
"\n",
"* **[Programación imperativa](http://es.wikipedia.org/wiki/Programaci%C3%B3n_imperativa)**: Este suele ser el primer *[paradigma](http://es.wikipedia.org/wiki/Paradigma_de_programaci%C3%B3n)* con el que nos encontramos, el mismo describe a la programación en términos de un conjunto de intrucciones que modifican el *estado* del programa y especifican claramente *cómo* se deben realizar las cosas y modificar ese *estado*. Este *[paradigma](http://es.wikipedia.org/wiki/Paradigma_de_programaci%C3%B3n)* esta representado por el lenguaje C.\n",
"\n",
"\n",
"* **[Programación lógica](http://es.wikipedia.org/wiki/Programaci%C3%B3n_l%C3%B3gica)**: En este *[paradigma](http://es.wikipedia.org/wiki/Paradigma_de_programaci%C3%B3n)* los programas son escritos en forma *[declarativa](http://es.wikipedia.org/wiki/Programaci%C3%B3n_declarativa)* utilizando expresiones lógicas. El principal exponente es el lenguaje [Prolog](http://es.wikipedia.org/wiki/Prolog) (programar en este esotérico lenguaje suele ser una experiencia interesante!).\n",
"\n",
"\n",
"* **[Programación Orientada a Objetos](http://es.wikipedia.org/wiki/Programaci%C3%B3n_orientada_a_objetos)**: La idea básica detrás de este *[paradigma](http://es.wikipedia.org/wiki/Paradigma_de_programaci%C3%B3n)* es que tanto los datos como las funciones que operan sobre estos datos deben estar contenidos en un mismo *objeto*. Estos *objetos* son entidades que tienen un determinado *estado, comportamiento (método) e identidad*. La [Programación Orientada a Objetos](http://es.wikipedia.org/wiki/Programaci%C3%B3n_orientada_a_objetos) es sumamente utilizada en el desarrollo de software actual; uno de sus principales impulsores es el lenguaje de programación Java.\n",
"\n",
"\n",
"* **[Programación Funcional](http://es.wikipedia.org/wiki/Programaci%C3%B3n_funcional)**: Este último *[paradigma](http://es.wikipedia.org/wiki/Paradigma_de_programaci%C3%B3n)* enfatiza la utilización de *funciones puras*, es decir, funciones que no tengan efectos secundarios, que no manejan datos mutables o de *estado*. Esta en clara contraposición con la *[programación imperativa](http://es.wikipedia.org/wiki/Programaci%C3%B3n_imperativa)*. Uno de sus principales representantes es el lenguaje [Haskell](https://wiki.haskell.org/Haskell) (lenguaje, que compite en belleza, elegancia y expresividad con [Python](http://python.org/)!).\n",
"\n",
"La mayoría de los lenguajes modernos son multiparadigma, es decir, nos permiten programar utilizando más de uno de los *[paradigmas](http://es.wikipedia.org/wiki/Paradigma_de_programaci%C3%B3n)* arriba descritos. En este artículo voy a intentar explicar como podemos aplicar la [Programación Funcional](http://es.wikipedia.org/wiki/Programaci%C3%B3n_funcional) con [Python](http://python.org/)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## ¿Por qué Programación Funcional?\n",
"\n",
"En estos últimos años hemos visto el resurgimiento de la [Programación Funcional](http://es.wikipedia.org/wiki/Programaci%C3%B3n_funcional), nuevos lenguajes como [Scala](http://www.scala-lang.org/) y [Apple Swift](https://developer.apple.com/swift/) ya traen por defecto montones de herramientas para facilitar el *[paradigma](http://es.wikipedia.org/wiki/Paradigma_de_programaci%C3%B3n) funcional*. La principales razones del crecimiento de la popularidad de la [Programación Funcional](http://es.wikipedia.org/wiki/Programaci%C3%B3n_funcional) son:\n",
"\n",
"1. Los programas escritos en un *estilo funcional* son más fáciles de testear y [depurar](http://es.wikipedia.org/wiki/Depuraci%C3%B3n_de_programas).\n",
"2. Por su característica modular facilita la [computación concurrente](http://es.wikipedia.org/wiki/Computaci%C3%B3n_concurrente) y [paralela](http://es.wikipedia.org/wiki/Computaci%C3%B3n_paralela); permitiendonos obtener muchas más ventajas de los [procesadores multinúcleo](http://es.wikipedia.org/wiki/Procesador_multin%C3%BAcleo) modernos.\n",
"3. El *estilo funcional* se lleva muy bien con los datos; permitiendonos crear algoritmos y programas más expresivos para manejar la enorme cantidad de datos de la [Big Data](http://es.wikipedia.org/wiki/Big_data).(Aplicar el estilo funcional me suele recordar a utilizar las formulas en [Excel](http://relopezbriega.com.ar/excel/))."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Programación Funcional con Python\n",
"\n",
"Antes de comenzar con ejemplos les voy a mencionar algunos de los modulos que que nos facilitan la [Programación Funcional](http://es.wikipedia.org/wiki/Programaci%C3%B3n_funcional) en [Python](http://python.org/), ellos son:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* **[Intertools](https://docs.python.org/3/library/itertools.html)**: Este es un modulo que viene ya instalado con la distribución oficial de [Python](http://python.org/); nos brinda un gran número de herramientas para facilitarnos la creación de [iteradores](http://en.wikipedia.org/wiki/Iterator). \n",
"\n",
"\n",
"* **[Operator](https://docs.python.org/3/library/operator.html)**: Este modulo también la vamos a encontrar ya instalado con [Python](http://python.org/), en el vamos a poder encontrar a los principales operadores de [Python](http://python.org/) convertidos en funciones.\n",
"\n",
"\n",
"* **[Functools](https://docs.python.org/3/library/functools.html)**: También ya incluido dentro de [Python](http://python.org/) este modulo nos ayuda a crear *[Funciones de orden superior](http://es.wikipedia.org/wiki/Funci%C3%B3n_de_orden_superior)*, es decir, funciones que actuan sobre o nos devuelven otras funciones.\n",
"\n",
"\n",
"* **[Fn](https://github.com/kachayev/fn.py)**: Este modulo, creado por [Alexey Kachayev](https://github.com/kachayev), brinda a [Python](http://python.org/) las *\"baterías\"* adicionales para hacer el *estilo funcional* de programación mucho más fácil.\n",
"\n",
"\n",
"* **[Cytoolz](https://github.com/pytoolz/cytoolz)**: Modulo creado por [Erik Welch](https://github.com/eriknw) que también nos proporciona varias herramientas para la [Programación Funcional](http://es.wikipedia.org/wiki/Programaci%C3%B3n_funcional), especialmente orientado a operaciones de análisis de datos.\n",
"\n",
"\n",
"* **[Macropy](https://github.com/lihaoyi/macropy)**: Este modulo, creado por [Li Haoyi](https://github.com/lihaoyi) trae a [Python](http://python.org/) características propias de los *lenguajes puramente funcionales*, como ser, *[pattern matching](http://es.wikipedia.org/wiki/B%C3%BAsqueda_de_patrones)*, *[tail call optimization](http://en.wikipedia.org/wiki/Tail_call)*, y *[case classes](http://en.wikipedia.org/wiki/Scala_%28programming_language%29#Case_classes_and_pattern_matching)*."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Ejemplos"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Utilizando Map, Reduce, Filter y Zip\n",
"\n",
"Cuando tenemos que realizar operaciones sobre *listas*, en lugar de utilizar los clásicos *loops*, podemos utilizar las funciones Map, Reduce, Filter y Zip.\n",
"\n",
"#### Map\n",
"\n",
"La función Map nos permite aplicar una operación sobre cada uno de los items de una *lista*. El primer argumento es la *función* que vamos a aplicar y el segundo argumento es la lista."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#creamos una lista de números del 1 al 10\n",
"items = list(xrange(1, 11))\n",
"items"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#creamos una lista de los cuadrados de la lista items.\n",
"#forma imperativa.\n",
"cuadrados = []\n",
"for i in items:\n",
" cuadrados.append(i ** 2)\n",
" \n",
"cuadrados"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#Cuadrados utilizando Map.\n",
"#forma funcional\n",
"cuadrados = map(lambda x: x **2, items)\n",
"cuadrados"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Como podemos ver, al utilizar map las líneas de código se reducen y nuestro programa es mucho más simple de comprender. En el ejemplo le estamos pasando a map una *función anónima* o *lambda*. Esta es otra característica que nos ofrece [Python](http://python.org/) para la [Programación Funcional](http://es.wikipedia.org/wiki/Programaci%C3%B3n_funcional).\n",
"Map también puede ser utilizado con funciones de más de un argumento y más de una lista, por ejemplo:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"#importamos pow.\n",
"from math import pow"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"8.0"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#como vemos la función pow toma dos argumentos, un número y su potencia.\n",
"pow(2, 3)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"#si tenemos las siguientes listas \n",
"numeros = [2, 3, 4]\n",
"potencias = [3, 2, 4]"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[8.0, 9.0, 256.0]"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#podemos aplicar map con pow y las dos listas.\n",
"#nos devolvera una sola lista con las potencias aplicadas sobre los números.\n",
"potenciados = map(pow, numeros, potencias)\n",
"potenciados"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Reduce\n",
"\n",
"La función Reduce reduce los valores de la *lista* a un solo valor aplicando una *funcion reductora*. El primer argumento es la *función reductora* que vamos a aplicar y el segundo argumento es la lista."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"55"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#Sumando los valores de la lista items.\n",
"#forma imperativa\n",
"suma = 0\n",
"for i in items:\n",
" suma += i\n",
"\n",
"suma"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"55"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#Suma utilizando Reduce.\n",
"#Forma funcional\n",
"from functools import reduce #en python3 reduce se encuentra en modulo functools\n",
"\n",
"suma = reduce(lambda x, y: x + y, items)\n",
"suma"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"La función Reduce también cuenta con un tercer argumento que es el valor inicial o default. Por ejemplo si quisiéramos sumarle 10 a la suma de los elementos de la lista items, solo tendríamos que agregar el tercer argumento."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"65"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#10 + suma items\n",
"suma10 = reduce(lambda x, y: x + y, items, 10)\n",
"suma10"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Filter\n",
"\n",
"La función Filter nos ofrece una forma muy elegante de filtrar elementos de una lista.El primer argumento es la *función filtradora* que vamos a aplicar y el segundo argumento es la lista."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[2, 4, 6, 8, 10]"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#Numeros pares de la lista items.\n",
"#Forma imperativa.\n",
"pares = []\n",
"for i in items:\n",
" if i % 2 ==0:\n",
" pares.append(i)\n",
" \n",
"pares"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[2, 4, 6, 8, 10]"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#Pares utilizando Filter\n",
"#Forma funcional.\n",
"pares = filter(lambda x: x % 2 == 0, items)\n",
"pares"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Zip\n",
"\n",
"Zip es una función para reorganizar listas. Como parámetros admite un conjunto de listas. Lo hace es tomar el elemento *iésimo* de cada lista y unirlos en una *[tupla](http://es.wikipedia.org/wiki/Tupla)*, después une todas las *[tuplas](http://es.wikipedia.org/wiki/Tupla)* en una sola lista."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"#Ejemplo de zip\n",
"nombres = [\"Raul\", \"Pedro\", \"Sofia\"]\n",
"apellidos = [\"Lopez Briega\", \"Perez\", \"Gonzalez\"]"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[('Raul', 'Lopez Briega'), ('Pedro', 'Perez'), ('Sofia', 'Gonzalez')]"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#zip une cada nombre con su apellido en una lista de tuplas.\n",
"nombreApellido = zip(nombres, apellidos)\n",
"nombreApellido"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Removiendo Efectos Secundarios\n",
"\n",
"Una de las buenas practicas que hace al *estilo funcional* es siempre tratar de evitar los *efectos secundarios*, es decir, evitar que nuestras funciones modifiquen los valores de sus parámetros, así en lugar de escribir código como el siguiente: "
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"#Funcion que no sigue las buenas practias de la programacion funcional.\n",
"#Esta funcion tiene efectos secundarios, ya que modifica la lista que se le pasa como argumento.\n",
"def cuadrados(lista):\n",
" for i, v in enumerate(lista):\n",
" lista[i] = v ** 2\n",
" return lista"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Deberíamos escribir código como el siguiente, el cual evita los efectos secundarios:"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"#Version funcional de la funcion anterior.\n",
"def fcuadrados(lista):\n",
" return map(lambda x: x ** 2, lista)"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#Aplicando fcuadrados sobre items.\n",
"fcuadrados(items)"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#items no se modifico\n",
"items"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#aplicando cuadrados sobre items\n",
"cuadrados(items)"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#Esta función tiene efecto secundario.\n",
"#items fue modificado por cuadrados.\n",
"items"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Al escribir funciones que no tengan efectos secundarios nos vamos a ahorrar muchos dolores de cabeza ocasionados por la modificación involuntaria de objetos."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Utilizando el modulo Fn.py\n",
"\n",
"Algunas de las cosas que nos ofrece este modulo son: Estructuras de datos inmutables, lambdas al estilo de [Scala](http://www.scala-lang.org/), *[lazy evaluation](http://es.wikipedia.org/wiki/Evaluaci%C3%B3n_perezosa)* de *[streams](http://en.wikipedia.org/wiki/Stream_processing)*, nuevas *[Funciones de orden superior](http://es.wikipedia.org/wiki/Funci%C3%B3n_de_orden_superior)*, entre otras."
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"13"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#Lambdas al estilo scala\n",
"from fn import _\n",
"\n",
"(_ + _)(10, 3)"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"items = list(xrange(1,11))"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"cuadrados = map( _ ** 2, items)\n",
"cuadrados"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
""
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#Streams\n",
"from fn import Stream\n",
"\n",
"s = Stream() << [1,2,3,4,5]\n",
"s"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[1, 2, 3, 4, 5]"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"list(s)"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"2"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"s[1]"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
""
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"s << [6, 7, 8, 9]"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"7"
]
},
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"s[6]"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#Stream fibonacci\n",
"from fn.iters import take, drop, map as imap\n",
"from operator import add\n",
"\n",
"f = Stream()\n",
"fib = f << [0, 1] << imap(add, f, drop(1, f))\n",
"\n",
"#primeros 10 elementos de fibonacci\n",
"list(take(10, fib)) "
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"6765"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#elemento 20 de la secuencia fibonacci\n",
"fib[20] "
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[102334155, 165580141, 267914296, 433494437, 701408733]"
]
},
"execution_count": 31,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#elementos 40 al 45 de la secuencia fibonacci\n",
"list(fib[40:45]) "
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"11"
]
},
"execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#Funciones de orden superior\n",
"from fn import F\n",
"from operator import add, mul #operadores de suma y multiplicacion\n",
"\n",
"#composición de funciones\n",
"F(add, 1)(10)"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"#f es una funcion que llama a otra funcion.\n",
"f = F(add, 5) << F(mul, 100) #<< operador de composicion de funciones."
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[5, 105, 205, 305]"
]
},
"execution_count": 34,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#cada valor de la lista primero se multiplica por 100 y luego\n",
"#se le suma 5, segun composicion de f de arriba.\n",
"map(f, [0, 1, 2, 3])"
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"func = F() >> (filter, _ < 6) >> sum"
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"15"
]
},
"execution_count": 36,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#func primero filtra los valores menores a 6\n",
"#y luego los suma.\n",
"func(xrange(10))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Utilizando el modulo cytoolz\n",
"\n",
"Este modulo nos provee varias herramienta para trabajar con funciones, iteradores y diccionarios."
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"#Datos a utilizar en los ejemplos\n",
"cuentas = [(1, 'Alice', 100, 'F'), # id, nombre, balance, sexo\n",
" (2, 'Bob', 200, 'M'),\n",
" (3, 'Charlie', 150, 'M'),\n",
" (4, 'Dennis', 50, 'M'),\n",
" (5, 'Edith', 300, 'F')]"
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[('Bob', 200), ('Edith', 300)]"
]
},
"execution_count": 38,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from cytoolz.curried import pipe, map as cmap, filter as cfilter, get\n",
"#seleccionando el id y el nombre de los que tienen un balance mayor a 150\n",
"pipe(cuentas, cfilter(lambda (id, nombre, balance, sexo): balance > 150),\n",
" cmap(get([1, 2])),\n",
" list)"
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[('Bob', 200), ('Edith', 300)]"
]
},
"execution_count": 39,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#este mismo resultado tambien lo podemos lograr con las listas por comprensión.\n",
"#mas pythonico.\n",
"[(nombre, balance) for (id, nombre, balance, sexo) in cuentas \n",
" if balance > 150]"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"{'F': [(1, 'Alice', 100, 'F'), (5, 'Edith', 300, 'F')],\n",
" 'M': [(2, 'Bob', 200, 'M'), (3, 'Charlie', 150, 'M'), (4, 'Dennis', 50, 'M')]}"
]
},
"execution_count": 40,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from cytoolz import groupby\n",
"\n",
"#agrupando por sexo \n",
"groupby(get(3), cuentas)"
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"{False: 4, True: 6}"
]
},
"execution_count": 41,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#utilizando reduceby\n",
"from cytoolz import reduceby\n",
"\n",
"def iseven(n):\n",
" return n % 2 == 0\n",
"\n",
"def add(x, y):\n",
" return x + y\n",
"\n",
"reduceby(iseven, add, [1, 2, 3, 4])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Ordenando objectos con operator itemgetter, attrgetter y methodcaller\n",
"\n",
"Existen tres funciones dignas de mención en el modulo operator, las cuales nos permiten ordenar todo tipo de objetos en forma muy sencilla, ellas son itemgetter, attrgetter y methodcaller."
]
},
{
"cell_type": "code",
"execution_count": 42,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"#Datos para los ejemplos\n",
"estudiantes_tupla = [\n",
" ('john', 'A', 15),\n",
" ('jane', 'B', 12),\n",
" ('dave', 'B', 10),\n",
"]\n",
"\n",
"class Estudiante:\n",
" def __init__(self, nombre, nota, edad):\n",
" self.nombre = nombre\n",
" self.nota = nota\n",
" self.edad = edad\n",
" def __repr__(self):\n",
" return repr((self.nombre, self.nota, self.edad))\n",
" def nota_ponderada(self):\n",
" return 'CBA'.index(self.nota) / float(self.edad)\n",
" \n",
"estudiantes_objeto = [\n",
" Estudiante('john', 'A', 15),\n",
" Estudiante('jane', 'B', 12),\n",
" Estudiante('dave', 'B', 10),\n",
"]"
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]"
]
},
"execution_count": 43,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from operator import itemgetter, attrgetter, methodcaller\n",
"\n",
"#ordenar por edad tupla\n",
"sorted(estudiantes_tupla, key=itemgetter(2))"
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]"
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#ordenar por edad objetos\n",
"sorted(estudiantes_objeto, key=attrgetter('edad'))"
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]"
]
},
"execution_count": 45,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#ordenar por nota y edad tupla\n",
"sorted(estudiantes_tupla, key=itemgetter(1,2))"
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]"
]
},
"execution_count": 46,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#ordenar por nota y edad objetos\n",
"sorted(estudiantes_objeto, key=attrgetter('nota', 'edad'))"
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[('jane', 'B', 12), ('dave', 'B', 10), ('john', 'A', 15)]"
]
},
"execution_count": 47,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#ordenando por el resultado del metodo nota_ponderada\n",
"sorted(estudiantes_objeto, key=methodcaller('nota_ponderada'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"Hasta aquí llega esta introducción. Tengan en cuenta que [Python](http://python.org/) no es un lenguaje puramente funcional, por lo que algunas soluciones pueden verse más como un *hack* y no ser del todo *pythonicas*. El concepto más importante es el de evitar los ***efectos secundarios*** en nuestras funciones. Debemos mantener un equilibrio entre los diferentes *paradigmas* y utilizar las opciones que nos ofrece [Python](http://python.org/) que haga más legible nuestro código. Para más información sobre la [Programación Funcional](http://es.wikipedia.org/wiki/Programaci%C3%B3n_funcional) en [Python](http://python.org/) también puede visitar el siguiente [documento](https://docs.python.org/2/howto/functional.html#introduction) y darse una vuelta por la documentación de los módulos mencionados más arriba. Por último, los que quieran incursionar con un lenguaje puramente funcional, les recomiendo [Haskell](https://wiki.haskell.org/Haskell)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Saludos!\n",
"\n",
"*Este post fue escrito utilizando IPython notebook. Pueden descargar este [notebook](https://github.com/relopezbriega/relopezbriega.github.io/blob/master/downloads/FunctProgramPython.ipynb) o ver su version estática en [nbviewer](http://nbviewer.ipython.org/github/relopezbriega/relopezbriega.github.io/blob/master/downloads/FunctProgramPython.ipynb).*"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.3+"
}
},
"nbformat": 4,
"nbformat_minor": 0
}