?
C, FORTRAN…
var l = [1, 2, 3, 4, 5] var gt2 = [] var el while (el = l.shift()) { if (el > 2) { gt2.push(el) } } // gt2 == [3, 4, 5]
Lenguajes:
Algunas funciones matemáticas
a = (r) => π * r * r
c = (r) => 2 * π * r
const add1 = (x) => x + 1
const add1_ = (x) => { switch (x) { // ... case -1: return 0 case 0: return 1 case 1: return 2 case 2: return 3 // ... } }
const add1_ = (x) => { switch (x) { // ... case -1: return 0 case 0: return 1 case 1: return 2 case 2: return 3 // ... } }
Los principios funcionales reducen complejidad
Lenguajes funcionales ofrecen tipado muy potente
Aprender un lenguaje funcional te obliga a pensar de una manera radicalmente distinta. Es un gran ejercicio mental. Ganas otra perspectiva desde la cual aproximarte a una problemática.
Learn […] one language that emphasizes class abstractions […], one that emphasizes functional abstraction (like Lisp or ML or Haskell), one that supports syntactic abstraction (like Lisp), one that supports declarative specifications […], and one that emphasizes parallelism (like Clojure or Go).
"Teach Yourself Programming in Ten Years", Peter Norvig, Director of Research Google
A partir de una lista de usuarios, crear una tabla html.
const users = [ { id: '87e2fbc2', name: 'Frank Underwood', occupation: 'Whip', transactions: [ 100, -10, 20, -99 ], friends: [ 'bc15b67f', '6ba21fbd', '9aa690e0' ] }, // ... ]
<!-- ... --> <tr> <td>87e2fbc25</td> <td>Frank Underwood</td> <td>Whip</td> <td>11 €</td> <td><ul> <li>Zoe Barnes</li> <li>Claire Underwood</li> <li>Doug Stamper</li> </ul></td> </tr> <!-- ... -->
const users2html = (us) => { let rowHtml = ''; for (let i=0; i<us.length; i++) { const u = us[i] let colHtml = '<td>'+u.id+'</td>' + '<td>'+u.name+'</td>' + '<td>'+u.occupation+'</td>' let balance = 0 for (let j=0; j<u.transactions.length; j++) { balance += u.transactions[j] } colHtml += '<td>' + balance + ' €</td>' let friendsHtml = '' for (let j=0; j<us.length; j++) { const f = us[j] if (u.friends.indexOf(f.id) > -1) { friendsHtml += '<li>' + f.name + '</li>' } } colHtml += '<td><ul>' + friendsHtml + '</ul></td>' rowHtml += '<tr>' + colHtml + '</tr>' } return '<tbody>' + rowHtml + '</tbody>' }
Combinar 2 funciones para crear otra
compose :: (b -> c) -> (a -> b) -> a -> c h = compose(g, f) h(x) === g(f(x))
Filtrar elementos
filter :: (a -> Bool) -> [a] -> [a] filter(() => true, [1, 2, 3]) === [1, 2, 3] filter(() => false, [1, 2, 3]) === []
Crear un nuevo array aplicando aplicando una funcion
map :: (a -> b) -> [a] -> [b] map((x) => x + 1, [1, 2, 3]) === [2, 3, 4]
Combinar todos los elements del array para llegar a 1 valor
reduce :: (a -> b -> a) -> a -> [b] -> a reduce((x, y) => x + y, 0, [1, 2, 3]) === 6
let balance = 0 for (let j=0; j<u.transactions.length; j++) { const t = u.transactions[j] balance += t } colHtml += '<td>' + balance + ' €</td>'
const balance = reduce((a, b) => a + b, 0, u.transactions)
let fusers = [] for (let j=0; j<us.length; j++) { const fuser = us[j] if (u.friends.indexOf(fuser.id) > -1) fusers.push(fuser) }
const fusers = filter(fuser => u.friends.indexOf(fuser.id) > -1, us)
let rowHtml = '' for (let i=0; i<us.length; i++) { const u = us[i] // ... rowHtml = rowHtml + '<tr>' + colHtml + '</tr>' }
let balance = 0 for (let j=0; j<u.transactions.length; j++) { balance = balance + u.transactions[j] }
let friendsHtml = '' for (let j=0; j<fusers.length; j++) { const f = fusers[j] friendsHtml = friendsHtml + '<li>' + f.name + '</li>' }
const add = (a, b) => a + b const elem = (x, xs) => -1 < xs.indexOf(x) const wrap = (el) => (t) => `<${el}>${t}</${el}>` const td = wrap('td') const u2html = (us) => (u) => { const balance = reduce(add, 0, u.transactions) + ' €' const friendsHtml = wrap('ul')( reduce((html,f) => elem(f.id, u.friends) ? html + wrap('li')(f.name) : html , '', us) ) const colHtml = map(td, [u.id, u.name, u.occupation, balance, friendsHtml]).join('') return wrap('tr')(colHtml) } const users3html = (us) => wrap('tbody')(map(u2html(us), us).join(''))
const users2html = (us) => { let rowHtml = ''; for (let i=0; i<us.length; i++) { const u = us[i] let colHtml = '<td>'+u.id+'</td>' + '<td>'+u.name+'</td>' + '<td>'+u.occupation+'</td>' let balance = 0 for (let j=0; j<u.transactions.length; j++) { const t = u.transactions[j] balance += t } colHtml += '<td>' + balance + ' €</td>' let friendsHtml = '' for (let j=0; j<us.length; j++) { const f = us[j] if (u.friends.indexOf(f.id) > -1) { friendsHtml += '<li>' + f.name + '</li>' } } colHtml += '<td><ul>' + friendsHtml + '</ul></td>' rowHtml += '<tr>' + colHtml + '</tr>' } return '<tbody>' + rowHtml + '</tbody>' }
const map = (f, xs) => 0 === xs.length ? [] : [f(xs[0])].concat(map(f, xs.slice(1)))
const filter = (pred, xs) => 0 === xs.length ? [] : pred(xs[0]) ? [xs[0]].concat(filter(pred, xs.slice(1))) : filter(pred, xs.slice(1))
const reduce = (f, acc, xs) => 0 === xs.length ? acc : reduce(f, f(acc, xs[0]), xs.slice(1))
const map = (f, xs) => 0 === xs.length ? [] : [f(xs[0])].concat(map(f, xs.slice(1)))
const filter = (pred, xs) => 0 === xs.length ? [] : pred(xs[0]) ? [xs[0]].concat(filter(pred, xs.slice(1))) : filter(pred, xs.slice(1))
const map_ = (f, xs) => reduce((acc, x) => acc.concat(f(x)), [], xs)
const filter_ = (pred, xs) => reduce((acc, x) => pred(x) ? acc.concat(x) : acc, [], xs)
Funciones que reciben o devuelven funciones.
const logInOut = (f) => (...args) => { const result = f(...args) console.log("%s(%s) = %O", f.name, args.join(', '), result) return result } const add = (x, y) => x + y const logAdd = logInOut(add) var x = logAdd(5, 10) // loguea "add(5, 10) = 15" // x === 15
logInOut(add)(5, 10) // loguea "add(5, 10) = 15" // returns 15
La recursión es una estratégia que aproxima a la solucion a través de soluciónes reducidas sucesivas, hasta encontrar un caso base (que no puede reducirse).
const fibo = (n) => n < 1 ? 0 : n < 3 ? 1 : fibo(n-1) + fibo(n-2)
La "captura" de variables de un scope superior.
const initCounter = (init) => { // ^^^ scope de init vvv return () => init++ // ^^^ scope de init vvv } x = initCounter(-10) y = initCounter(5) x() // -10 x() // -9 y() // 5 y() // 6
const memo = (f) => { _memo = {} return (...args) => { const input = JSON.stringify(args) return _memo[input] ? _memo[input] : _memo[input] = f(...args) } } const fibo_ = memo( (n) => n < 1 ? 0 : n < 3 ? 1 : fibo_(n-1) + fibo_(n-2) )
No es más que una función anónima.
Tiene gran utilidad cuando se combina con funciones higher-order.
// | ------------------- λ ------------------- | setTimeout(() => console.log('El tiempo se ha acabado!'), 1000)
const fibo_ = memo( // | ---------------------- λ ------------------------- | (n) => n < 1 ? 0 : n < 3 ? 1 : fibo_(n-1) + fibo_(n-2) ) // | ----------------- λ ------------------- | $('a').click(function(e) { $(this).addClass('clicked') }) // Patrón de módulo // IIFE / IIλ // λ() ;(function() { const exports = {} // exports.someFunction = ... }())
Funciones pueden ser combinados para crear otras funciones.
const compose = (f, g) => (...args) => f(g(...args)) const product = (a, b) => a * b const square = (x) => product(x, x) const double = (x) => product(2, x) const area = compose((x) => product(Math.PI, x), square)
"Guardamos" algunos parametros de una función
const apply = (f, x) => (y) => f(x, y) // ver Function.prototype.bind const double_ = (x) => product(2, x) // se convierte en const double = apply(product, 2) const area_ = compose((x) => product(Math.PI, x), square) // se convierte en const area = compose(apply(product, Math.PI), square)
Se dice que una función es "curried" cuando puede recibir sus argumentos de uno en uno.
Piensa en aplicación parcial automática.
// compara const product = (a, b) => a * b const productC = (a) => (b) => a * b const double = (x) = product(2, x) const double_ = productC(2)
Aprender haskell:
Aprender lisp (clojure) 4clojure