Почему явный возврат имеет значение в процессе?
-
07-07-2019 - |
Вопрос
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
.
Кто-нибудь знает, почему это так?
Решение
Ruby имеет три конструкции:
- A блок не является объектом и создается
{
...}
илиdo
...end
. - A процесс является
Proc
объект, созданныйProc.new
илиproc
. - A лямбда является
Proc
созданныйlambda
(илиproc
в Ruby 1.8).
В Ruby есть три ключевых слова, которые возвращают что-то:
return
завершает работу метода или лямбды, в котором он находится.next
завершает блок, процедуру или лямбду, в котором он находится.break
завершает работу метода, который выдал блок или вызвал процедуру или лямбда-выражение, в котором он находится.
В лямбдах, return
ведет себя как next
, по какой бы то ни было причине. next
и break
названы так, как они есть, потому что они чаще всего используются с такими методами, как each
, где завершение блока приведет к возобновлению итерации с Далее элемент коллекции и завершающий each
это заставит вас перерыв вырванный из цикла.
Если вы используете
return
внутри определения foo
, вы вернетесь из foo
, даже если он находится внутри блока или процедуры.Чтобы вернуться из блока, вы можете использовать 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"
Другие советы
Это семантика для Proc
s;это не обязательно семантика для всех блоков.Я согласен, это немного сбивает с толку.Это делается для дополнительной гибкости (и, возможно, частично потому, что у Ruby нет спецификации, кроме как для его реализации).
Поведение определено в Proc
реализация. Lambda
вы ведете себя по-другому, поэтому, если вы хотите, чтобы ваш return
s не выходить вне заключающего метода используйте лямбды.Или опустите 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)
Подумайте об этом так: 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 и перейдет к следующему утверждению.