?
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