Ruby Lambda против Proc LocalJumpError - PullRequest
4 голосов
/ 17 ноября 2011

Ruby и StackOverflow новичок здесь, пробираясь через Ruby, столкнулся с моим первым серьезным препятствием. Мне действительно тяжело оборачиваться вокруг Проков и Лямбд. Вот код, с которым я работаю.

def procBuilder(message)
  Proc.new{ puts message; return}
end

def test
  puts "entering method"
  p = procBuilder("entering proc")
  p.call
  puts "exit method"
end

test

По замыслу это выдает ошибку LocalJumpError, но я не совсем понимаю, почему. Если бы мне нужно было угадать, что это сделало, я бы предположил, что при запуске p = procBuilder («вхождение proc») при запуске p = procBuilder сначала будет напечатано сообщение об ошибке, а p.call выдаст ошибку, так как p.call не передает строку , но явно я упускаю что-то критическое, что происходит между этими двумя строками Я также не совсем понимаю, почему это работает с лямбдой, а не с процедурой, но я думаю, что понимание ошибки решит и эту проблему.

Заранее спасибо за разъяснения

Ответы [ 2 ]

4 голосов
/ 17 ноября 2011

Вот ответ Я дал на связанный вопрос. В нем немного рассказывается о лямбда против proc и LocalJumpErrors.

В процедуре return - это специальный фрагмент синтаксиса, который возвращается из лексической области действия процедуры, а не самого процесса. Поэтому он пытается вернуться из procBuilder, который уже вышел.

Есть несколько способов исправить это:

  1. Не используйте return вообще. Ruby вернет управление вызывающему процессу самостоятельно.
  2. Измените proc на lambda, который ведет себя так, как вы ожидаете. Лямбды действуют как методы; процы действуют как блоки.

Что касается ошибки, которую вы ожидаете , вы не должны этого понимать. procBuilder возвращает процедуру, которая содержит переменную сообщения. Вам не нужны никакие аргументы для самого процесса.

Редактировать : ответ на дополнительный вопрос. Процесс закрывается. Он «захватил» переменную сообщения (локальная переменная в procBuilder), которая находилась в области действия при создании процедуры. Proc теперь может бродить по вашей программе со скрытой переменной сообщения, готовой к печати, когда вы ее вызываете. Единственная проблема - оператор return, который имеет дополнительное требование, чтобы лексическая область видимости оставалась «живой».

Причина всего этого в том, что такое поведение действительно полезно в блоках. В этом случае это совсем не полезно, поэтому вам следует просто использовать lambda, где return означает что-то менее безумное.

Действительно отличный учебник по замыканиям в ruby: http://innig.net/software/ruby/closures-in-ruby.rb

0 голосов
/ 18 мая 2016

Важным различием между процедурой и методом или лямбда является то, как они обрабатывают оператор return.Если метод определен внутри другого метода, оператор return во внутреннем методе завершается только из самого внутреннего метода, тогда внешний метод продолжает выполняться.То же самое касается определения лямбды в лямбде, лямбда в методе или метода в лямбда.Однако, когда в методе определен процесс, оператор return будет выходить из процесса, а также из внешнего (включающего) метода.Пример:

def meditate
    puts "Adjusting posture…"
    p = Proc.new { puts "Ringing bell…"; return }
    p.call
    puts "Sitting still…"  # This is not executed
end

meditate

Output:
Adjusting posture…
Ringing bell…

Обратите внимание, что последняя строка метода не была выполнена, потому что оператор return внутри proc вышел из метода proc и включающего метода.

Если мы определимproc без включающего (внешнего) метода и использующего оператор return, он выдаст LocalJumpError.

p = Proc.new { puts "Ringing bell…"; return }
p.call
Output:
Ringing bell…
LocalJumpError: unexpected return

Это происходит потому, что когда оператор return достигается внутри proc, а не возвращается из контекста, в котором онбыл вызван, он возвращается из области, в которой он (proc) был определен.В следующем примере LocalJumpError происходит, потому что процесс пытается вернуться из среды верхнего уровня, где он был определен.

def meditate p
    puts "Adjusting posture…"
    p.call
    puts "Sitting still…"  # This is not executed
end

p = Proc.new { puts "Ringing bell…"; return }

meditate p
Output:
Adjusting posture…
Ringing bell…
LocalJumpError: unexpected return

Обычно не рекомендуется использовать оператор return вProc.Процессы обычно передаются между методами, и если метод, для которого был определен протокол, уже возвращен, он выдаст исключение.В приведенном ниже примере мы можем просто удалить оператор return.Однако есть случаи, когда нам действительно нужно что-то вернуть.В последнем случае, вероятно, лучше использовать лямбду вместо процедуры.Позже мы увидим, что лямбда-выражения обрабатывают операторы возврата другим способом, более похожим на методы.

Ниже приведен еще один сценарий с оператором return:

def zafu_factory
    # This method will return the following proc implicitly
    Proc.new { puts "Round black zafu"; return }
end

def meditate
    puts "Adjusting posture…"
    p = zafu_factory
    p.call
    puts "Sitting still…"  # This is not executed
end

meditate
Output:
Adjusting posture…
Round black zafu
LocalJumpError: unexpected return

Что только что произошло?Метод zafu_factory создал и неявно возвратил процесс.Затем метод вызывался процедурой, и когда был достигнут оператор возврата внутри процедуры, он пытался вернуться из контекста, в котором он был определен (метод zafu_factory).Однако zafu_factory уже возвратил proc, и метод может возвращаться только один раз при каждом вызове.Другими словами, было сгенерировано исключение, потому что метод zafu_factory уже вернулся при вызове proc и попытался вернуться во второй раз.

Подробнее в этом сообщении в блоге о Procs и Lambdas: замыкания в Ruby

...