Pregunta

def foo
  f = Proc.new { return "return from foo from inside proc" }
  f.call # control leaves foo here
  return "return from foo" 
end

def bar
  b = Proc.new { "return from bar from inside proc" }
  b.call # control leaves bar here
  return "return from bar" 
end

puts foo # prints "return from foo from inside proc" 
puts bar # prints "return from bar" 

Pensé que la palabra clave return era opcional en Ruby y que siempre está return si lo solicita o no. Dado eso, me parece sorprendente que foo y bar tengan una salida diferente determinada por el hecho de que foo contiene un return en Proc f .

¿Alguien sabe por qué este es el caso?

¿Fue útil?

Solución

Ruby tiene tres construcciones:

  1. Un bloque no es un objeto y es creado por { ... } o do .. . end .
  2. Un proc es un objeto Proc creado por Proc.new o proc .
  3. Un lambda es un Proc creado por lambda (o proc en Ruby 1.8).

Ruby tiene tres palabras clave que regresan de algo:

  1. return termina el método o lambda en el que se encuentra.
  2. next termina el bloque, proc o lambda en el que se encuentra.
  3. break termina el método que cedió al bloque o invocó el proceso o lambda en el que se encuentra.

En lambdas, return se comporta como next , por cualquier razón. next y break se nombran de la forma en que se usan porque se usan más comúnmente con métodos como each , donde la terminación del bloque hará que la iteración reanudar con el elemento siguiente de la colección, y terminar cada hará que rompa fuera del ciclo.


Si usa return dentro de la definición de foo , regresará de foo , incluso si está dentro de un bloque o un proceso. Para regresar de un bloque, puede usar la palabra clave next en su lugar.

def foo
  f = Proc.new { next "return from foo from inside proc" }
  f.call # control leaves foo here
  return "return from foo" 
end
puts foo # prints "return from foo"

Otros consejos

Esta es la semántica para Proc s; No es necesariamente la semántica para todos los bloques. Estoy de acuerdo en que esto es un poco confuso. Está allí para mayor flexibilidad (y quizás en parte porque Ruby no tiene especificaciones, excepto por su implementación).

El comportamiento se define en la implementación Proc . Lambda s se comportan de manera diferente, por lo que si desea que su return s no salga del método de inclusión, utilice lambdas . O bien, omita la palabra clave return de su Proc .

Una investigación profunda de los cierres de Rubys está aquí . Es una exposición fantástica.

Entonces:

def foo   
  f = Proc.new {
    p2 = Proc.new { return "inner proc"};
    p2.call
    return "proc"
  }
  f.call
  return "foo"
end

def foo2
  result = Proc.new{"proc"}.call
  "foo2 (proc result is: #{result})"
end

def bar
  l = lambda { return "lambda" }
  result = l.call
  return "bar (lambda result is: #{result})"
end

puts foo
# inner proc
puts foo2
# foo (proc result is: proc) 
puts bar
# bar (lambda result is: lambda) 

Piénselo de esta manera: Proc.new solo cree un bloque de código que sea parte de la función de llamada. proc / lambda crea una función anónima que tiene enlaces especiales. Unos pequeños ejemplos de código ayudarán:

def foo
  f = Proc.new { return "return from foo from inside Proc.new" }
  f.call # control leaves foo here
  return "return from foo" 
end

es equivalente a

def foo
  begin
    return "return from foo from inside begin/end" }
  end

  return "return from foo" 
end

así que está claro que el retorno solo regresará de la función 'foo'

en contraste:

def foo
  f = proc { return "return from foo from inside proc" }
  f.call # control stasy in foo here
  return "return from foo" 
end

es equivalente a (ignorando los enlaces ya que no se usa en este ejemplo):

def unonymous_proc
  return "return from foo from inside proc"
end

def foo
  unonymous_proc()
  return "return from foo" 
end

Que es tan claro que no regresará de foo y continuará con la siguiente declaración.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top