Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Pensando Funcionalmente

Pensando Funcionalmente

Una intro a los beneficios de la programación funcional, con breves ejemplos en Python, Ruby y Clojure.

Denis Fuenzalida

August 27, 2013
Tweet

More Decks by Denis Fuenzalida

Other Decks in Programming

Transcript

  1. • • Problema: no hay definición Problema: no hay definición

    universalmente universalmente aceptada aceptada • • Funciones como elementos de primer nivel del lenguaje: Funciones como elementos de primer nivel del lenguaje: “ “First Class Functions First Class Functions” ” → → es posible almacenar es posible almacenar referencias referencias a funciones a funciones pasar funciones como → pasar funciones como → parámetros parámetros → → crear funciones que retornan crear funciones que retornan funciones funciones Definición Definición
  2. • • Mayor nivel de abstracción: Mayor nivel de abstracción:

    más énfasis en el → más énfasis en el → qué qué y no en el y no en el cómo cómo → → menos código que comprender menos código que comprender → → a veces, código más genérico y re-utilizable a veces, código más genérico y re-utilizable • • En En Clojure Clojure se prefieren funciones puras* se prefieren funciones puras* son más fáciles de entender → son más fáciles de entender → → → más fáciles de testear más fáciles de testear → → se pueden guardar se pueden guardar en caché en caché y paralelizar y paralelizar fácilmente fácilmente Beneficios Beneficios
  3. // Basado en StringUtils.java de Apache Commons Lang // Basado

    en StringUtils.java de Apache Commons Lang /* /* * Spec: * Spec: * * * StringUtils.indexOfAny(null, *) = -1 * StringUtils.indexOfAny(null, *) = -1 * StringUtils.indexOfAny("", *) = -1 * StringUtils.indexOfAny("", *) = -1 * StringUtils.indexOfAny(*, null) = -1 * StringUtils.indexOfAny(*, null) = -1 * StringUtils.indexOfAny(*, []) = -1 * StringUtils.indexOfAny(*, []) = -1 * StringUtils.indexOfAny(" * StringUtils.indexOfAny("z zzabyycdxx",['z','a']) = 0 zabyycdxx",['z','a']) = 0 * StringUtils.indexOfAny("zza * StringUtils.indexOfAny("zzab byycdxx",['b','y']) = 3 yycdxx",['b','y']) = 3 * StringUtils.indexOfAny("aba", ['z']) = -1 * StringUtils.indexOfAny("aba", ['z']) = -1 */ */
  4. // Basado en StringUtils.java de Apache Commons Lang // Basado

    en StringUtils.java de Apache Commons Lang class class StringUtils { StringUtils { public static public static int int indexOfAny indexOfAny(String (String str str, char[] , char[] toSearch toSearch){ ){ if if (isEmpty(str) || ArrayUtils.isEmpty(toSearch)){ (isEmpty(str) || ArrayUtils.isEmpty(toSearch)){ return return -1; -1; } } for for (int i = 0; i < str.length(); i++){ (int i = 0; i < str.length(); i++){ char char ch ch = str.charAt(i); = str.charAt(i); for for (int (int j j = 0; j < toSearch.length; j++){ = 0; j < toSearch.length; j++){ if if (searchChars[j] == ch){ (searchChars[j] == ch){ return return i; i; } } } } } } return return -1; -1; } } } }
  5. // Simplificando condiciones de borde... // Simplificando condiciones de borde...

    class class StringUtils { StringUtils { public static public static int int indexOfAny indexOfAny(String (String str str, char[] , char[] toSearch toSearch){ ){ when (toSearch) { when (toSearch) { for for (int i = 0; i < str.length(); i++){ (int i = 0; i < str.length(); i++){ char char ch ch = str.charAt(i); = str.charAt(i); for for (int (int j j = 0; j < toSearch.length; j++){ = 0; j < toSearch.length; j++){ if if (searchChars[j] == ch){ (searchChars[j] == ch){ return return i; i; } } } } } } } } } } } }
  6. // En un lenguaje dinámico* podemos quitar tipos/clases: // En

    un lenguaje dinámico* podemos quitar tipos/clases: indexOfAny indexOfAny( (str str, , toSearch toSearch){ ){ when (toSearch) { when (toSearch) { for for (i = 0; i < str.length(); i++){ (i = 0; i < str.length(); i++){ ch ch = str.charAt(i); = str.charAt(i); for for ( (j j = 0; j < toSearch.length; j++){ = 0; j < toSearch.length; j++){ if if (searchChars[j] == ch){ (searchChars[j] == ch){ return return i; i; } } } } } } } } } }
  7. // pseudo-codigo // pseudo-codigo indexOfAny indexOfAny( (str str, , toSearch

    toSearch){ ){ when (toSearch) { when (toSearch) { for for (i = 0; i < str.length(); i++){ (i = 0; i < str.length(); i++){ ch ch = str.charAt(i); = str.charAt(i); when toSearch(ch) i; // Busca 'ch', retorna i when toSearch(ch) i; // Busca 'ch', retorna i } } } } } }
  8. // Usando una list-comprehension para iterar // Usando una list-comprehension

    para iterar indexOfAny indexOfAny( (str str, , toSearch toSearch){ ){ when (toSearch) { when (toSearch) { for for ([i, ch] in indexed(str)){ ([i, ch] in indexed(str)){ when toSearch(ch) i; when toSearch(ch) i; } } } } } }
  9. // pseudo-codigo // pseudo-codigo indexOfAny indexOfAny( (str str, , toSearch

    toSearch){ ){ when (toSearch) { when (toSearch) { for for ([i, ch] in indexed(str)){ ([i, ch] in indexed(str)){ when toSearch(ch) i; when toSearch(ch) i; } } } } } } ;; Version Clojure ;; Version Clojure ( (defn defn index-filter index-filter [coll pred] [coll pred] ( (when when pred pred ( (for for [[idx elt] (indexed coll) [[idx elt] (indexed coll) :when :when (pred elt)] idx))) (pred elt)] idx)))
  10. Imperativa Funcional Funciones 1 1 Clases 1 0 Puntos internos

    de retorno 2 0 Variables 3 0 Ramificaciones 4 0 Operaciones booleanas 1 0 Llamadas a funciones 6 3 Total 18 4 Complejidad de cada versión Complejidad de cada versión
  11. ;; Es correcta esta implementación? ;; Es correcta esta implementación?

    ( (defn defn indexed indexed [s] ( [s] (map map vector ( vector (iterate iterate inc 0) s)) inc 0) s)) ( (defn defn index-filter index-filter [coll pred] [coll pred] ( (when when pred pred ( (for for [[idx elt] (indexed coll) [[idx elt] (indexed coll) :when :when (pred elt)] idx))) (pred elt)] idx))) ;; StringUtils.indexOfAny("zzabyycdxx",['z','a']) = 0 ;; StringUtils.indexOfAny("zzabyycdxx",['z','a']) = 0 ;; StringUtils.indexOfAny("zzabyycdxx",['b','y']) = 3 ;; StringUtils.indexOfAny("zzabyycdxx",['b','y']) = 3 (index-filter "zzabyycdxx" #{\z \a}) (index-filter "zzabyycdxx" #{\z \a}) ;; => ( ;; => (0 0 1 2) 1 2) (index-filter "zzabyycdxx" #{\b \y}) (index-filter "zzabyycdxx" #{\b \y}) ;; => ( ;; => (3 3 4 5) 4 5)
  12. ;; index-filter retorna una lista* de TODOS los calces! ;;

    index-filter retorna una lista* de TODOS los calces! ;; Indices de 'cara' en una lista de tiradas de moneda: ;; Indices de 'cara' en una lista de tiradas de moneda: (index-filter [ (index-filter [:c :s :c :c :s :s :c :s :c :s :c :c :s :c :c :s :s :c :s :c :s :c] #{ ] #{:c :c}) }) ;; => (0 2 3 6 8 10) ;; => (0 2 3 6 8 10) ;; Cuál es el primer número de Fibonacci mayor que 1000? ;; Cuál es el primer número de Fibonacci mayor que 1000? ( (defn defn fibo fibo [] [] ( (map map first ( first (iterate iterate ( (fn fn [[a b]] [b (+ a b)]) [0 1]))) [[a b]] [b (+ a b)]) [0 1]))) ( (first first (index-filter (fibo) #(> % 1000))) (index-filter (fibo) #(> % 1000))) ;; => 17 ;; => 17 ( (nth nth (fibo) 17) (fibo) 17) ;; => 1597 ;; => 1597
  13. ¿Qué versión es más general? ¿Qué versión es más general?

    Imperativa Funcional Busca dentro de Strings Busca en Secuencias Busca sólo caracteres Busca usando cualquier función predicado Retorna el primer calce Retorna una lista lazy de todos los calces
  14. Euler #4: Euler #4: Mayor producto palíndromo Mayor producto palíndromo

    • • Número palíndromo: si es el mismo, escrito al revés Número palíndromo: si es el mismo, escrito al revés (1.234.321) (1.234.321) • • El mayor palíndromo que es producto de 2 números El mayor palíndromo que es producto de 2 números de 2 cifras es de 2 cifras es 9009 9009 = 91 x 99 = 91 x 99 • • Encontrar el mayor producto de números de 3 cifras Encontrar el mayor producto de números de 3 cifras que sea palíndromo que sea palíndromo
  15. // Palindromos.java // Palindromos.java public class public class Palindromos {

    Palindromos { public static public static boolean boolean isPalin isPalin(Integer (Integer n n) { ) { String String ns ns = n.toString(); = n.toString(); String String reverse reverse = new StringBuffer(ns).reverse().toString(); = new StringBuffer(ns).reverse().toString(); return return nstring.equals(reverse); nstring.equals(reverse); } } public static public static void main(String[] void main(String[] args args) { ) { Integer Integer max max = 0; = 0; for for (int (int i i = 100; i <= 999; i++) { = 100; i <= 999; i++) { for for (int (int j j = 100; j <= 999; j++) { = 100; j <= 999; j++) { Integer Integer prod prod = i * j; = i * j; if if (isPalin(prod) && prod > max) { (isPalin(prod) && prod > max) { max = prod; max = prod; } } } } } } System.out.println(max); System.out.println(max); } } } }
  16. # euler-004.py # euler-004.py def def palin(x): palin(x): return return

    str str(x) == (x) == str str(x)[::-1] (x)[::-1] palins palins = [x*y = [x*y for for x x in in range(100, 1000) \ range(100, 1000) \ for for y y in in range(x, 1000) range(x, 1000) if if palin(x*y)] palin(x*y)] print(max(palins)) print(max(palins))
  17. # euler-004.rb # euler-004.rb puts (100..999).flat_map{|a| (a..999).flat_map {|b| a*b}} puts

    (100..999).flat_map{|a| (a..999).flat_map {|b| a*b}} .select{|x| x.to_s == x.to_s.reverse} .select{|x| x.to_s == x.to_s.reverse} .max .max # euler-004.groovy # euler-004.groovy def def max max = (100..999).collect{x->(100..999).collect{y->x*y}} = (100..999).collect{x->(100..999).collect{y->x*y}} .flatten() .flatten() .findAll{ it.toString() == it.toString().reverse() } .findAll{ it.toString() == it.toString().reverse() } .max() .max() println max println max
  18. ;; euler-004.clj ;; euler-004.clj ( (defn defn palin? [x] palin?

    [x] (= ( (= (str str x) ( x) (apply str apply str ( (reverse reverse ( (str str x))))) x))))) ( (println println ( (reduce reduce max max ( (for for [x ( [x (range range 100 1000) 100 1000) y ( y (range range x 1000) x 1000) :let :let [prod (* x y)] [prod (* x y)] :when :when (palin? prod)] prod)))) (palin? prod)] prod))))
  19. • • Mayor nivel de abstracción: Mayor nivel de abstracción:

    más énfasis en el → más énfasis en el → qué qué y no en el y no en el cómo cómo → → menos código que comprender menos código que comprender → → a veces, código más genérico y re-utilizable a veces, código más genérico y re-utilizable • • En En Clojure Clojure se prefieren funciones puras* se prefieren funciones puras* son más fáciles de entender → son más fáciles de entender → → → más fáciles de testear (no se necesita más fáciles de testear (no se necesita mocking mocking) ) → → se pueden guardar se pueden guardar en caché en caché (memoize f) (memoize f) → → se pueden paralelizar se pueden paralelizar fácilmente fácilmente (pmap f) (pmap f) Beneficios de PF Beneficios de PF
  20. Créditos Créditos Portada - “Lights of Ideas” por Saad Faruque

    http://www.flickr.com/photos/cblue98/7254347346/sizes/l/ Fondo - “Gravel Background” por Ember Studio http://www.flickr.com/photos/48013511@N07/7685947954/ Ejemplos en Clojure basados en “Functional Thinking” por Neal Ford http://shop.oreilly.com/product/0636920030393.do