2012-03-01 7 views
16

Quiero ser capaz de llamar a un lambda anónimo desde dentro usando Ruby. Considere el siguiente bloque recursivo (devuelve un factorial). Sé que puedo asignar a una variable, y esa variable está dentro del alcance de la lambda:¿Puedo hacer referencia a una lambda desde dentro usando Ruby?

fac = lambda { |n| n == 1 ? 1 : n * fac.call(n - 1) } 
fac.call(5) 

Pero, yo quiero ser capaz de hacer lo siguiente (por ninguna razón práctica hasta el momento, I' m simplemente interesados ​​en explorar el lenguaje un poco más):

(lambda { |n| n == 1 ? 1 : n * self.call(n - 1) }).call(5) 

que no va a funcionar, porque el objeto es selfmain. ¿Lo estoy haciendo mal? ¿Estoy tratando de hacer algo que no es posible? De lo contrario, ¿es por alguna restricción teórica o simplemente no se implementa en Ruby?

+5

¿Está familiarizado con combinador Y? Puede que no sea la mejor solución práctica, pero desde el punto de vista teórico es muy interesante. Si no lo hace, eche un vistazo a [este artículo] (http://nex-3.com/posts/43-fun-with-the-y-combinator-in-ruby). Ten cuidado, podría volar tu cerebro. –

Respuesta

5

Parece que la función anónima realmente no tiene ninguna referencia. Puede comprobarlo por callee

lambda{ __callee__ }.call #=> nil 

Y sin referencia no se puede llamar a esta función. puedo proponer a sólo un poco más limpia variante:

(fac = lambda{ |n| n==1 ? 1 : n*fac.call(n-1) }).call(5) 
+1

Sería * realmente * mucho más limpio crear una función nombrada para eso. –

+0

Sí, mi versión original incluía la lambda entre paréntesis y ejecuté un .call() allí. Sin embargo, esta no es una cuestión de brevedad del código, sino más bien de cuán profundo puede ser el agujero funcional de conejo que Ruby puede llegar.KL-7 tiene un comentario anterior que enlaza con un artículo que describe el combinador Y, que es una lectura muy interesante. –

7

En el siguiente ejemplo, el lambda está aún en el anonimato, pero tiene una referencia. (¿Tiene que pasar por el anonimato?)

(l = lambda { l.call }).call 

(B. Gracias a Niklas por señalar el error en mi primera respuesta; sólo había probado en el IRB y funcionó allí).

Esto, por supuesto, termina en un error SystemStackError: stack level too deep, pero demuestra el propósito.

1

Además de KL-7's comment, he aquí una solución Y Combinator:

lambda { |f| 
    lambda { |x| x.call(x) }.call(
    lambda { |x| f.call(lambda { |v| x.call(x).call(v) }) }) 
}.call(
    lambda { |f| 
    lambda { |n| n == 0 ? 1 : n * f.call(n - 1) } 
    } 
).call(5) #=> 120 

que normalmente dividir éstas:

y = lambda { |f| 
    lambda { |x| x.call(x) }.call(
    lambda { |x| f.call(lambda { |v| x.call(x).call(v) }) }) 
} 

fac = y.call(
    lambda { |f| lambda { |n| n == 0 ? 1 : n * f.call(n - 1) } } 
) 

fac.call(5) #=> 120 

en cuenta que aunque se está asignando fac, no se utiliza dentro de la lambda .

que haría uso de la sintaxis de Ruby -> y .() en lugar de .call():

y = ->(f) { 
    ->(x) { x.(x) }.(
    ->(x) { f.(->(v) { x.(x).(v) }) }) 
} 

fac = y.(->(f) { 
    ->(n) { n == 0 ? 1 : n * f.(n - 1) } 
}) 

fac.(5) #=> 120 

La invocación y se puede simplificar un poco mediante el uso de curry:

y = ->(f) { 
    ->(x) { x.(x) }.(
    ->(x) { f.curry.(->(v) { x.(x).(v) }) }) 
} 

fac = y.(
    ->(f, n) { n == 0 ? 1 : n * f.(n - 1) } 
) 

fac.(5) #=> 120 
Cuestiones relacionadas