Почему явный возврат имеет значение в Proc?
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"
Я думал, что ключевое слово return
не является обязательным в Ruby и что вы всегда return
, запрашиваете ли вы его или нет. Учитывая это, я нахожу удивительным, что foo
и bar
имеют различный вывод, определяемый тем фактом, что foo
содержит явный return
в Proc f
.
Кто-нибудь знает, почему это так?
Ответы
Ответ 1
Ruby имеет три конструкции:
- Блок не является объектом и создается
{
... }
или do
... end
.
- Проком является объект
Proc
, созданный Proc.new
или Proc
.
- lambda - это
Proc
, созданный lambda
(или Proc
в Ruby 1.8).
Ruby имеет три ключевых слова, которые возвращаются от чего-то:
-
return
завершает метод или лямбда, в котором он находится.
-
next
завершает блок, proc или лямбда, в котором он находится.
-
break
завершает метод, который уступает блоку или вызывается proc или лямбда, в котором он находится.
В lambdas return
ведет себя как next
по любой причине. next
и break
называются так, как они есть, потому что они наиболее часто используются с такими методами, как each
, где завершение блока приведет к возобновлению итерации с помощью элемента next коллекции, и завершение each
приведет к выходу break из цикла.
Если вы используете return
внутри определения foo
, вы вернетесь из foo
, даже если он находится внутри блока или proc. Чтобы вернуться из блока, вы можете использовать ключевое слово next
.
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"
Ответ 2
Это семантика для Proc
s; это не обязательно семантика для всех блоков. Я согласен, что это немного запутанно. Там есть дополнительная гибкость (и, возможно, частично причина Ruby не имеет спецификации, кроме ее реализации).
Поведение определено в реализации Proc
. Lambda
ведут себя по-другому, поэтому, если вы хотите, чтобы ваш return
не вышел из из метода размещения, используйте lambdas. Или опустите ключевое слово return
из своего Proc
.
Глубокое исследование закрытия Rubys здесь. Это фантастический рассказ.
Итак:
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)
Ответ 3
Подумайте об этом так: Proc.new просто создайте блок кода, который является частью вызывающей функции. proc/lambda создает анонимную функцию со специальными привязками. Немного примеров кода помогут:
def foo
f = Proc.new { return "return from foo from inside Proc.new" }
f.call # control leaves foo here
return "return from foo"
end
эквивалентно
def foo
begin
return "return from foo from inside begin/end" }
end
return "return from foo"
end
поэтому ясно, что возврат будет просто возвращаться из функции 'foo'
в отличие:
def foo
f = proc { return "return from foo from inside proc" }
f.call # control stasy in foo here
return "return from foo"
end
эквивалентно (игнорируя привязки, так как не используется в этом примере):
def unonymous_proc
return "return from foo from inside proc"
end
def foo
unonymous_proc()
return "return from foo"
end
Это так же явно не вернется из foo и не перейдет к следующему утверждению.