pensando funcionalmente

25
Pensando Funcionalmente Pensando Funcionalmente Denis Fuenzalida – Agosto de 2013 Denis Fuenzalida – Agosto de 2013 [email protected] [email protected]

Upload: denis-fuenzalida

Post on 13-Jan-2015

144 views

Category:

Technology


3 download

DESCRIPTION

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

TRANSCRIPT

Page 1: Pensando funcionalmente

Pensando FuncionalmentePensando FuncionalmenteDenis Fuenzalida – Agosto de 2013Denis Fuenzalida – Agosto de 2013

[email protected]@gmail.com

Page 2: Pensando funcionalmente

Programación funcional?Programación funcional?

Page 3: Pensando funcionalmente

• • Problema: no hay definición Problema: no hay definición universalmenteuniversalmente aceptada aceptada

• • Funciones como elementos de primer nivel del lenguaje:Funciones como elementos de primer nivel del lenguaje: “ “First Class FunctionsFirst Class Functions”” → → es posible almacenar es posible almacenar referenciasreferencias a funciones a funciones pasar funciones como → pasar funciones como → parámetrosparámetros → → crear funciones que retornan crear funciones que retornan funcionesfunciones

DefiniciónDefinición

Page 4: Pensando funcionalmente

• • 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ómocómo → → menos código que comprendermenos código que comprender → → a veces, código más genérico y re-utilizablea veces, código más genérico y re-utilizable

• • En En ClojureClojure 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 testearmás fáciles de testear → → se pueden guardar se pueden guardar en cachéen caché y paralelizar y paralelizar fácilmentefácilmente

BeneficiosBeneficios

Page 5: Pensando funcionalmente

Convertir de código Convertir de código imperativo a funcionalimperativo a funcional

Page 6: Pensando funcionalmente

// 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("zzzabyycdxx",['z','a']) = 0zabyycdxx",['z','a']) = 0 * StringUtils.indexOfAny("zza* StringUtils.indexOfAny("zzabbyycdxx",['b','y']) = 3yycdxx",['b','y']) = 3 * StringUtils.indexOfAny("aba", ['z']) = -1* StringUtils.indexOfAny("aba", ['z']) = -1 */*/

Page 7: Pensando funcionalmente

// Basado en StringUtils.java de Apache Commons Lang// Basado en StringUtils.java de Apache Commons Lang

classclass StringUtils { StringUtils {

public staticpublic static int int indexOfAnyindexOfAny(String (String strstr, char[] , char[] toSearchtoSearch){){

ifif (isEmpty(str) || ArrayUtils.isEmpty(toSearch)){ (isEmpty(str) || ArrayUtils.isEmpty(toSearch)){ returnreturn -1; -1; }}

forfor (int i = 0; i < str.length(); i++){ (int i = 0; i < str.length(); i++){ char char chch = str.charAt(i); = str.charAt(i); forfor (int (int jj = 0; j < toSearch.length; j++){ = 0; j < toSearch.length; j++){ ifif (searchChars[j] == ch){ (searchChars[j] == ch){ returnreturn i; i; }} }} }} returnreturn -1; -1; }}}}

Page 8: Pensando funcionalmente

// Simplificando condiciones de borde...// Simplificando condiciones de borde...

classclass StringUtils { StringUtils {

public staticpublic static int int indexOfAnyindexOfAny(String (String strstr, char[] , char[] toSearchtoSearch){){

when (toSearch) {when (toSearch) {

forfor (int i = 0; i < str.length(); i++){ (int i = 0; i < str.length(); i++){ char char chch = str.charAt(i); = str.charAt(i); forfor (int (int jj = 0; j < toSearch.length; j++){ = 0; j < toSearch.length; j++){ ifif (searchChars[j] == ch){ (searchChars[j] == ch){ returnreturn i; i; }} }} }} }} }}}}

Page 9: Pensando funcionalmente

// En un lenguaje dinámico* podemos quitar tipos/clases:// En un lenguaje dinámico* podemos quitar tipos/clases:

indexOfAnyindexOfAny((strstr, , toSearchtoSearch){){

when (toSearch) {when (toSearch) { forfor (i = 0; i < str.length(); i++){ (i = 0; i < str.length(); i++){ chch = str.charAt(i); = str.charAt(i); forfor ( (jj = 0; j < toSearch.length; j++){ = 0; j < toSearch.length; j++){ ifif (searchChars[j] == ch){ (searchChars[j] == ch){ returnreturn i; i; }} }} }} }} }}

Page 10: Pensando funcionalmente

// pseudo-codigo// pseudo-codigo

indexOfAnyindexOfAny((strstr, , toSearchtoSearch){){

when (toSearch) {when (toSearch) { forfor (i = 0; i < str.length(); i++){ (i = 0; i < str.length(); i++){ chch = str.charAt(i); = str.charAt(i); when toSearch(ch) i; // Busca 'ch', retorna iwhen toSearch(ch) i; // Busca 'ch', retorna i }} }} }}

Page 11: Pensando funcionalmente

// Usando una list-comprehension para iterar// Usando una list-comprehension para iterar

indexOfAnyindexOfAny((strstr, , toSearchtoSearch){){

when (toSearch) {when (toSearch) { forfor ([i, ch] in indexed(str)){ ([i, ch] in indexed(str)){ when toSearch(ch) i;when toSearch(ch) i; }} }} }}

Page 12: Pensando funcionalmente

// pseudo-codigo// pseudo-codigo

indexOfAnyindexOfAny((strstr, , toSearchtoSearch){){

when (toSearch) {when (toSearch) { forfor ([i, ch] in indexed(str)){ ([i, ch] in indexed(str)){ when toSearch(ch) i;when toSearch(ch) i; }} }} }}

;; Version Clojure;; Version Clojure

((defndefn index-filterindex-filter [coll pred] [coll pred] ( (whenwhen pred pred ((forfor [[idx elt] (indexed coll) [[idx elt] (indexed coll) :when:when (pred elt)] idx))) (pred elt)] idx)))

Page 13: Pensando funcionalmente

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ónComplejidad de cada versión

Page 14: Pensando funcionalmente

;; Es correcta esta implementación?;; Es correcta esta implementación?

((defndefn indexedindexed [s] ( [s] (mapmap vector ( vector (iterateiterate inc 0) s)) inc 0) s))

((defndefn index-filterindex-filter [coll pred] [coll pred] ( (whenwhen pred pred ((forfor [[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});; => (;; => (00 1 2) 1 2)

(index-filter "zzabyycdxx" #{\b \y})(index-filter "zzabyycdxx" #{\b \y});; => (;; => (33 4 5) 4 5)

Page 15: Pensando funcionalmente

;; 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?((defndefn fibofibo [] [] ((mapmap first ( first (iterateiterate ( (fnfn [[a b]] [b (+ a b)]) [0 1]))) [[a b]] [b (+ a b)]) [0 1])))

((firstfirst (index-filter (fibo) #(> % 1000))) (index-filter (fibo) #(> % 1000)));; => 17;; => 17

((nthnth (fibo) 17) (fibo) 17);; => 1597;; => 1597

Page 16: Pensando funcionalmente

¿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

Page 17: Pensando funcionalmente

Euler #4: Euler #4: Mayor producto palíndromoMayor producto palíndromo

• • Número palíndromo: si es el mismo, escrito al revésNú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úmerosEl mayor palíndromo que es producto de 2 números de 2 cifras es de 2 cifras es 90099009 = 91 x 99 = 91 x 99

• • Encontrar el mayor producto de números de 3 cifrasEncontrar el mayor producto de números de 3 cifras que sea palíndromo que sea palíndromo

Page 18: Pensando funcionalmente

// Palindromos.java// Palindromos.java

public classpublic class Palindromos { Palindromos {

public staticpublic static boolean boolean isPalinisPalin(Integer (Integer nn) {) { String String nsns = n.toString(); = n.toString(); String String reversereverse = new StringBuffer(ns).reverse().toString(); = new StringBuffer(ns).reverse().toString(); returnreturn nstring.equals(reverse); nstring.equals(reverse); }}

public staticpublic static void main(String[] void main(String[] argsargs) {) { Integer Integer maxmax = 0; = 0; forfor (int (int ii = 100; i <= 999; i++) { = 100; i <= 999; i++) { forfor (int (int jj = 100; j <= 999; j++) { = 100; j <= 999; j++) { Integer Integer prodprod = i * j; = i * j; ifif (isPalin(prod) && prod > max) { (isPalin(prod) && prod > max) { max = prod;max = prod; }} }} }} System.out.println(max);System.out.println(max); }}}}

Page 19: Pensando funcionalmente

# euler-004.py# euler-004.py

defdef palin(x): palin(x): returnreturn strstr(x) == (x) == strstr(x)[::-1](x)[::-1]

palinspalins = [x*y = [x*y forfor x x inin range(100, 1000) \ range(100, 1000) \ forfor y y inin range(x, 1000) range(x, 1000) ifif palin(x*y)] palin(x*y)]

print(max(palins))print(max(palins))

Page 20: Pensando funcionalmente

# 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

defdef maxmax = (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 maxprintln max

Page 21: Pensando funcionalmente

;; euler-004.clj;; euler-004.clj

((defndefn palin? [x] palin? [x] (= ((= (strstr x) ( x) (apply strapply str ( (reversereverse ( (strstr x))))) x)))))

((printlnprintln ((reducereduce max max ((forfor [x ( [x (rangerange 100 1000) 100 1000) y (y (rangerange x 1000) x 1000) :let:let [prod (* x y)] [prod (* x y)] :when:when (palin? prod)] prod)))) (palin? prod)] prod))))

Page 22: Pensando funcionalmente

ConclusiónConclusión

Page 23: Pensando funcionalmente

• • 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ómocómo → → menos código que comprendermenos código que comprender → → a veces, código más genérico y re-utilizablea veces, código más genérico y re-utilizable

• • En En ClojureClojure 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 mockingmocking)) → → 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 PFBeneficios de PF

Page 24: Pensando funcionalmente

FinFin

Page 25: Pensando funcionalmente

CréditosCréditosPortada - “Lights of Ideas” por Saad Faruquehttp://www.flickr.com/photos/cblue98/7254347346/sizes/l/

Fondo - “Gravel Background” por Ember Studiohttp://www.flickr.com/photos/48013511@N07/7685947954/

Ejemplos en Clojure basados en “Functional Thinking” por Neal Fordhttp://shop.oreilly.com/product/0636920030393.do