{ "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 }