Почему явный возврат имеет значение в Proc? - PullRequest
48 голосов
/ 17 сентября 2009
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.

Кто-нибудь знает, почему это так?

Ответы [ 3 ]

80 голосов
/ 17 сентября 2009

Ruby имеет три конструкции:

  1. A блок не является объектом и создается { ... } или do ... end.
  2. A proc - это объект Proc, созданный Proc.new или proc.
  3. A лямбда - это Proc, созданный lambda (или proc в Ruby 1.8).

В Ruby есть три ключевых слова, которые возвращают что-то:

  1. return завершает метод или лямбду, в которой он находится.
  2. next завершает блок, процесс или лямбду, в которой он находится.
  3. break завершает метод, который уступил блоку или вызвал процесс или лямбду, в которой он находится.

В лямбдах return ведет себя как next по любой причине. next и break названы так, как они есть, потому что они чаще всего используются с такими методами, как each, где завершение блока приведет к возобновлению итерации с элементом next коллекции, и завершение 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"
12 голосов
/ 17 сентября 2009

Это семантика для Proc с; это не обязательно семантика для всех блоков. Я согласен, что это немного сбивает с толку. Это сделано для дополнительной гибкости (и, возможно, отчасти потому, что у Ruby нет спецификаций, кроме как для его реализации).

Поведение определено в реализации Proc. Lambda s ведут себя по-другому, поэтому, если вы хотите, чтобы ваши return s не выходили из метода включения, используйте lambdas . Или пропустите ключевое слово return из вашего Proc.

Глубокое исследование закрытий Рубиса здесь . Это фантастическое разоблачение.

Итак:

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) 
7 голосов
/ 17 сентября 2009

Думайте об этом так: 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

так что ясно, что return просто вернется из функции '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 и перейдет к следующему утверждению.

...