function - Buena explicación de "Combinators"(Para no matemáticos)
lambda y-combinator (8)
¿Alguien tiene una buena explicación de "combinators" (Y-combinators etc. y NO de la compañía )?
Estoy buscando uno para el programador práctico que entiende recursividad y funciones de orden superior, pero no tiene una teoría sólida o antecedentes matemáticos.
(Nota: que estoy hablando de estas cosas )
A menos que esté profundamente metido en la teoría, puede considerar al combinador en Y como un buen truco con funciones, como las mónadas.
Las mónadas le permiten encadenar acciones, el combinador Y le permite definir funciones recursivas.
Python tiene soporte integrado para funciones auto recursivas, por lo que puedes definirlas sin Y:
> def fun():
> print "bla"
> fun()
> fun()
bla
bla
bla
...
fun
es accesible dentro de la fun
misma, así que podemos llamarla fácilmente.
Pero, ¿y si Python fuera diferente y la fun
no fuera accesible dentro de la fun
?
> def fun():
> print "bla"
> # what to do here? (cannot call fun!)
La solución es pasar la fun
como un argumento a la fun
:
> def fun(arg): # fun receives itself as argument
> print "bla"
> arg(arg) # to recur, fun calls itself, and passes itself along
Y Y lo hace posible:
> def Y(f):
> f(f)
> Y(fun)
bla
bla
bla
...
Todo lo hace llamar a una función consigo misma como argumento.
(No sé si esta definición de Y es 100% correcta, pero creo que es la idea general).
Cita Wikipedia:
Un combinador es una función de orden superior que utiliza solo la aplicación de funciones y los combinados definidos anteriormente para definir un resultado a partir de sus argumentos.
Ahora, que significa esto? Significa que un combinador es una función (la salida está determinada únicamente por su entrada) cuya entrada incluye una función como argumento.
¿Cómo son esas funciones y para qué se utilizan? Aquí hay unos ejemplos:
(fog)(x) = f(g(x))
Aquí o
es un combinador que toma 2 funciones, f
y g
, y devuelve una función como resultado, la composición de f
con g
, es decir, fog
.
Los combadores se pueden usar para ocultar la lógica. Digamos que tenemos un tipo de datos NumberUndefined
, donde NumberUndefined
puede tomar un valor numérico Num x
o un valor Undefined
, donde x
a es un Number
. Ahora queremos construir suma, resta, multiplicación y división para este nuevo tipo numérico. La semántica es la misma que para los de Number
excepto si Undefined
es una entrada, la salida también debe estar Undefined
y cuando se divide por el número 0
la salida también está Undefined
.
Uno podría escribir el código tedioso de la siguiente manera:
Undefined +'' num = Undefined
num +'' Undefined = Undefined
(Num x) +'' (Num y) = Num (x + y)
Undefined -'' num = Undefined
num -'' Undefined = Undefined
(Num x) -'' (Num y) = Num (x - y)
Undefined *'' num = Undefined
num *'' Undefined = Undefined
(Num x) *'' (Num y) = Num (x * y)
Undefined /'' num = Undefined
num /'' Undefined = Undefined
(Num x) /'' (Num y) = if y == 0 then Undefined else Num (x / y)
Observe cómo todos tienen la misma lógica con respecto a los valores de entrada no definidos. Solo la división hace un poco más. La solución es extraer la lógica haciéndola un combinador.
comb (~) Undefined num = Undefined
comb (~) num Undefined = Undefined
comb (~) (Num x) (Num y) = Num (x ~ y)
x +'' y = comb (+) x y
x -'' y = comb (-) x y
x *'' y = comb (*) x y
x /'' y = if y == Num 0 then Undefined else comb (/) x y
Esto se puede generalizar en la llamada Mónada Maybe
que los programadores usan en lenguajes funcionales como Haskell, pero no iré allí.
En breve, Y combinator es una función de orden superior que se utiliza para implementar la recursión en expresiones lambda (funciones anónimas). Ver el artículo http://mvanier.livejournal.com/2897.html por Mike Vanier: es una de las mejores explicaciones prácticas de este asunto que he visto.
Además, escanee los archivos SO:
Este es un buen article . Los ejemplos de código están en esquema, pero no deberían ser difíciles de seguir.
Este se ve muy bien: http://www.catonmat.net/blog/derivation-of-ycombinator/
Estoy bastante corto en teoría, pero puedo darte un ejemplo que despierta mi imaginación, lo que puede ser útil para ti. El combinador más simple e interesante es probablemente "prueba".
Espero que conozcas a Python
tru = lambda x,y: x
fls = lambda x,y: y
test = lambda l,m,n: l(m,n)
Uso:
>>> test(tru,"goto loop","break")
''goto loop''
>>> test(fls,"goto loop","break")
''break''
la prueba evalúa el segundo argumento si el primer argumento es verdadero, de lo contrario el tercero.
>>> x = tru
>>> test(x,"goto loop","break")
''goto loop''
Se pueden construir sistemas completos a partir de unos pocos combinadores básicos.
(Este ejemplo es más o menos copiado de Types and Programming Languages por Benjamin C. Pierce)
Reginald Braithwaite (alias Raganwald) ha estado escribiendo una gran serie de combinadores en Ruby en su nuevo blog, homoiconic .
Si bien, por lo que yo sé, no mira al Y-combinator, sí observa otros combinadores, por ejemplo:
- el Kestrel
- el Thrush
- el Cardinal
- el Cernícalo Obstinado
- otros pájaros extravagantes
y algunas publicaciones sobre cómo puedes use .
Un combinador es una función sin variables libres . Eso significa, entre otras cosas, que el combinador no tiene dependencias de cosas fuera de la función, solo en los parámetros de la función.
Usando F # esta es mi comprensión de los combinadores:
let sum a b = a + b;; //sum function (lambda)
En el caso anterior, sum es un combinador porque tanto a
como b
están vinculados a los parámetros de la función.
let sum3 a b c = sum((sum a b) c);;
La función anterior no es un combinador ya que usa sum
, que no es una variable vinculada (es decir, no proviene de ninguno de los parámetros).
Podemos hacer que sum3 sea un combinador simplemente pasando la función suma como uno de los parámetros:
let sum3 a b c sumFunc = sumFunc((sumFunc a b) c);;
De esta forma, sumFunc
está vinculado y, por lo tanto, toda la función es un combinador.
Entonces, esta es mi comprensión de los combinadores. Su significado, por otro lado, todavía se me escapa. Como otros señalaron, los combinadores de puntos fijos le permiten a uno expresar una función recursiva sin recursión explicit
. Es decir, en lugar de llamarse a sí mismo, la función recusiva llama a lambda que se pasa como uno de los argumentos.
Esta es una de las derivaciones combinadas más comprensibles que encontré: