2012-01-17 13 views
7

La sentencia break para los bloques (según The Ruby Programming Language) se define de la siguiente manera:¿Por qué la declaración de interrupción en ruby ​​se comporta de manera diferente cuando se utiliza Proc.new v. El signo de ampersand?

que hace que el bloque de volver a su repetidor y el repetidor para volver al método que se invoca.

Por lo tanto, cuando se ejecuta el siguiente código, se produce un LocalJumpError.

def test 
    puts "entering test method" 
    proc = Proc.new { puts "entering proc"; break } 
    proc.call # LocalJumpError: iterator has already returned 
    puts "exiting test method" 
end 
test 

Mientras que el código siguientes no hace lanzar una LocalJumpError. ¿Qué tiene de especial el signo de ampersand? ¿El signo ampersand no usa implícitamente Proc.new?

def iterator(&proc) 
    puts "entering iterator" 
    proc.call # invoke the proc 
    puts "exiting iterator" # Never executed if the proc breaks 
end 

def test 
    iterator { puts "entering proc"; break } 
end 
test 

En otras palabras, leo el signo de ampersand como un medio de alinear la llamada Proc.new. En ese punto, el comportamiento debería ser el mismo que el primer fragmento de código.

def iterator (p = Proc.new { puts "entering proc"; break}) 
... 
end 

Negación: estoy Novato aprendizaje de la lengua (rubí 1.9.2), y por lo tanto a apreciar las referencias y una sinopsis detallada.

+1

No hay tiempo para una respuesta correcta, pero se trata de alcance, no tanto un Proc o Lambda son especiales. – coreyward

+0

cuando tenga tiempo ... por favor visite esta pregunta nuevamente. Agradecería su visión –

+0

En lugar de 'Proc.new' intente' lambda'. – Casper

Respuesta

6

break hace que el bloque y la persona que llama del retorno bloque. En el siguiente código:

proc = Proc.new { break } 

El "Llamada" del bloque que se convierte en un objeto Proc es Proc.new. Se supone que break hace que regrese la persona que llama del bloque, pero Proc.new ya ha regresado.

En este código:

def iterator(&b); b.call; end 
iterator { break } 

El llamador del bloque es iterator, así que tiene iterator retorno.

+0

lo tengo ... esta es la mejor respuesta. Sin embargo, ¿no se convierten todos los bloques en código procesable a través de Proc.new? Por lo tanto, ¿el iterador (& b) no se convierte en iterador (b = Proc.new b)? –

+0

y Proc.new no son lo mismo. & es sintaxis central; Proc.new es un método de biblioteca. Puedes escribir tu propio Proc.new así: 'class Proc; def self.new (&b); b; end; end'. Pero no hay forma de que pueda implementar su propia sintaxis del núcleo (además de piratear el intérprete o usar un preprocesador). –

3

Aquí está el answer.

Ampersand se utiliza para convertir un proceso a un bloque y un bloque a un proceso.

me cambió el ejemplo con el fin de relacionarse con su caso:

def run_my_code(&my_code) 
puts 'before proc' 
my_code.call 
puts 'after proc' 
end 
run_my_code { puts "passing a block, accepting a proc"; break} 
=> before proc 
    passing a block, accepting a proc 

Como se puede ver que no alcanzó la 'después proc'

def run_my_code 
yield 
end 
my_proc = Proc.new { puts "passing a proc instead of block"; break} 
run_my_code &my_proc 
=> passing a proc instead of block 
    LocalJumpError: break from proc-closure 
    from (pry):75:in `block in <main>' 

En el segundo ejemplo tiene un proceso en el resultado, el proceso se rompe desde iterator y vuelve a la función test.

def iterator(&proc) 
    puts 'entering iterator' 
    proc.call 
    puts 'exiting iterator' 
end 

def test 
    puts 'before test' 
    iterator { puts 'entering proc'; break } 
    puts 'after test' 
end 

=>before test 
entering iterator 
entering proc 
after test 
+0

Si lo tengo correcto, en el primer ejemplo, la declaración de interrupción regresa de Proc.new (ya que es un iterador) y del bloque que lo encerró. Documenté la definición al principio de la pregunta, tal como lo entendí, por qué la primera falló. Pero, si en el segundo ejemplo, el signo de ampersand es azúcar sintáctica para Proc.new, entonces no entiendo, por qué no falla allí –

+0

He corregido la respuesta. – megas

+0

Entonces, si un bloque se ha convertido en un proceso, ¿no debería funcionar la sentencia break como el primer fragmento de código? Quiero decir, si básicamente estamos creando un Proc.new, ¿por qué se comporta de manera diferente a Proc.new que estaba en una línea separada sobre la llamada al método? –

0

Tiene que ver con la diferencia entre bloques, procs y lambdas - y sus respectivos ámbitos.

escribí un post sobre ello en el 2009 que le puede resultar útil: http://www.leonardoborges.com/writings/2009/07/22/procs-lambdas-blocks-whats-the-difference/

Espero que esto ayude.

+0

La publicación fue claramente perspicaz, pero dado que estoy usando un proceso en ambos casos, no estoy seguro de por qué Ruby está tratando de dar un trato especial a un proceso sobre otro. –

+0

Tiene que ver con la explicación de palabra clave de retorno en la publicación. break, como return en este caso significa romper el método de llamada, prueba en este caso. Sin embargo, no puede salirse de la prueba, ya que puede verificar haciendo un corte justo después de 'Proc.new {...}', que es diferente en su segundo fragmento, ya que regresará del método de llamada, iterador en este caso, devolver el control a la nueva versión de la prueba. – leonardoborges

+0

Entonces, si la prueba se envolvió en un método llamado test_test, la instrucción break en Proc.new no debería fallar? Aquí' cómo estoy leyendo el bloque de conversión a iterador proc def (&proc); llamada #implicity proc = Proc.new {proc}; ... fin En cuyo caso, el LocalJumpError debería suceder, al igual que en la prueba. –

Cuestiones relacionadas