блок ruby ​​и возвращение чего-либо из блока - PullRequest
5 голосов
/ 14 мая 2010

Я использую ruby ​​1.8.7.

p = lambda { return 10;}
def lab(block)
  puts 'before'
  puts block.call
  puts 'after'
end
lab p

Выше указанного кода:

before
10
after

Я преобразовал тот же код в этот

def lab(&block)
  puts 'before'
  puts block.call
  puts 'after'
end
lab { return 10; }

Теперь я получаю LocalJumpError: неожиданное возвращение.

Для меня оба кода делают одно и то же. Да, в первом случае я передаю проц, а во втором - блок. Но & block преобразует этот блок в proc. Так что proc.call должен вести себя так же.

И да, я видел этот пост Использование 'return' в блоке Ruby

Ответы [ 4 ]

8 голосов
/ 14 мая 2010

Когда вы передаете блок с &, вы конвертируете его в процесс. Важным моментом является то, что proc и лямбда отличаются (лямбда на самом деле является подклассом proc), особенно в том, как они справляются с возвратом.

Таким образом, ваш рефакторинг кода на самом деле эквивалентен:

p = Proc.new { return 10;}
def lab(block)
  puts 'before'
  puts block.call
  puts 'after'
end
lab p

, который также генерирует LocalJumpError.

И вот почему: возврат proc возвращается из лексической области видимости, а лямбда-код возвращается в область выполнения. Таким образом, в то время как лямбда возвращается к lab, переданный в нее proc возвращается во внешнюю область, в которой он был объявлен. Локальная ошибка перехода означает, что ей некуда деваться, потому что нет функции включения.

Язык программирования Ruby лучше всего говорит:

Procs имеет блочное поведение, а лямбды - как метод

Вы просто должны отслеживать, что вы используете, где. Как и предполагали другие, все, что вам нужно сделать, это сбросить return с вашего блока, и все будет работать как положено.

5 голосов
/ 14 мая 2010

return внутри блока вернется из метода, в котором находится блок, а не из блока. Чтобы вернуться из блока, используйте next (он назван так, потому что при использовании методов-итераторов, таких как each и map, возврат из блока в основном означает переход к следующей итерации цикла).

Обратите внимание, что когда возвращаемое значение является последним вычисленным выражением в блоке, вам вообще не нужен какой-либо оператор возврата, т. Е. lab { 10 } сделает то же самое.

0 голосов
/ 14 мая 2010

Я думаю, что вам просто нужно разыменовать блок перед тем, как передать его:

 foo = lambda { return 10 }

 def trace_block(&fn)
   puts 'before calling fn'
   puts fn.call
   puts 'after caling fn'
 end

 trace_block(&foo)

Выход:

  before calling fn
  10
  after caling fn

Дополнительная информация:

0 голосов
/ 14 мая 2010

Блок {} включает контекст, в котором он задан, поэтому return пытается вернуться из строки lab { return 10; }. Вы можете фактически выполнить эту работу (иногда даже в полезной форме), поместив эту строку внутри метода, который затем возвратится (то есть «после» не печатается).

Чтобы вернуть 10 в block.call, пропустите return (или подставьте next).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...