Bloque de Ruby y devolviendo algo de bloque.

Estoy usando ruby ​​1.8.7.

p = lambda { return 10;} def lab(block) puts 'before' puts block.call puts 'after' end lab p 

La salida del código anterior es

 before 10 after 

Refacté el mismo código en este

 def lab(&block) puts 'before' puts block.call puts 'after' end lab { return 10; } 

Ahora estoy recibiendo LocalJumpError: retorno inesperado.

Para mí tanto el código está haciendo lo mismo. Sí, en el primer caso estoy pasando un proceso y en el segundo caso estoy pasando un bloque. Pero & block convierte ese bloque en proc. Así que proc.call debería comportarse igual.

Y sí, he visto esta publicación usando ‘return’ en un bloque de Ruby

Cuando pasas el bloque con &, lo estás convirtiendo en un proc. El punto importante es que un proc y un lambda son diferentes (lambda es en realidad una subclase de proc), específicamente en cómo tratan el retorno.

Entonces su código refactorizado es en realidad el equivalente de:

 p = Proc.new { return 10;} def lab(block) puts 'before' puts block.call puts 'after' end lab p 

que también genera un LocalJumpError.

A continuación, se explica por qué: el retorno de un proceso se devuelve desde su ámbito léxico, pero un lambda vuelve a su ámbito de ejecución. Entonces, mientras que la lambda regresa al lab , el procedimiento que se le devuelve al ámbito externo en el que se declaró. El error de salto local significa que no tiene a dónde ir, porque no hay una función de cierre.

El lenguaje de progtwigción Ruby lo dice mejor:

Procs tiene un comportamiento de bloque y las lambdas tienen un comportamiento de método

Solo tienes que hacer un seguimiento de lo que estás usando donde. Como otros lo han sugerido, todo lo que necesita hacer es eliminar el return de su locking, y las cosas funcionarán según lo previsto.

return dentro de un bloque volverá del método en que se encuentra el bloque, no del bloque. Para regresar del bloque, use next (se llama así porque con iterador-métodos como each y el map regresa del bloque básicamente significa saltar a la siguiente iteración del bucle).

Tenga en cuenta que cuando el valor de retorno es la última expresión evaluada en el bloque, no necesita ningún tipo de statement de retorno, es decir, el lab { 10 } hará lo mismo.

El bloque {} incluye el contexto en el que se proporciona, por lo que la return intenta regresar desde la línea de lab { return 10; } lab { return 10; } . Realmente puede hacer que esto funcione (a veces incluso de una manera útil) colocando esa línea dentro de un método, que luego regresará (es decir, “después de” no se imprime).

Para devolver el 10 a block.call , omita la return (o sustituya a next ).

Creo que solo necesitas desreferenciar el bloque antes de pasarlo:

  foo = lambda { return 10 } def trace_block(&fn) puts 'before calling fn' puts fn.call puts 'after caling fn' end trace_block(&foo) 

Salida:

  before calling fn 10 after caling fn 

Más información:

  • Entendiendo Ruby Blocks, Procs y Lambdas
  • Ruby Blocks 101