Почему оператор break в ruby ведет себя по-разному при использовании Proc.new v. Знак амперсанда?
Оператор break для блоков (согласно языку программирования Ruby) определяется следующим образом:
он заставляет блок возвращаться к своему итератору, а итератор - к методу, который его вызвал.
Поэтому, когда следующий код выполняется, он приводит к LocalJumpError.
def test
puts "entering test method"
proc = Proc.new { puts "entering proc"; break }
proc.call # LocalJumpError: iterator has already returned
puts "exiting test method"
end
test
Пока следующий код не выдает LocalJumpError. Что особенного в знаке амперсанда? Разве знак амперсанда неявно использует Proc.new?
def iterator(&proc)
puts "entering iterator"
proc.call # invoke the proc
puts "exiting iterator" # Never executed if the proc breaks
end
def test
iterator { puts "entering proc"; break }
end
test
Другими словами, я читаю знак амперсанда как средство включения вызова Proc.new. В этот момент поведение должно быть таким же, как в первом фрагменте кода.
def iterator (p = Proc.new { puts "entering proc"; break})
...
end
Отказ от ответственности: я новичок в изучении языка (ruby 1.9.2), и поэтому буду признателен за ссылки и подробный синопсис.
3 ответа
break
возвращает блок и вызывающий блок. В следующем коде:
proc = Proc.new { break }
"Вызывающим" блоком, который преобразуется в объект Proc, является Proc.new. break
должен вернуть вызывающую часть блока, но Proc.new уже вернулся.
В этом коде:
def iterator(&b); b.call; end
iterator { break }
Вызывающая сторона блока iterator
так что делает iterator
вернуть.
Вот ответ.
Амперсанд используется для преобразования проц в блок, а блок в проц.
Я изменил пример так, чтобы он относился к вашему случаю:
def run_my_code(&my_code)
puts 'before proc'
my_code.call
puts 'after proc'
end
run_my_code { puts "passing a block, accepting a proc"; break}
=> before proc
passing a block, accepting a proc
Как вы можете видеть, он не достиг "после процедуры"
def run_my_code
yield
end
my_proc = Proc.new { puts "passing a proc instead of block"; break}
run_my_code &my_proc
=> passing a proc instead of block
LocalJumpError: break from proc-closure
from (pry):75:in `block in <main>'
Во втором примере у вас есть процесс в результате, процесс прерывается от iterator
и возвращается к test
функция.
def iterator(&proc)
puts 'entering iterator'
proc.call
puts 'exiting iterator'
end
def test
puts 'before test'
iterator { puts 'entering proc'; break }
puts 'after test'
end
=>before test
entering iterator
entering proc
after test
Это связано с разницей между блоками, проками и лямбдами - и их областями действия.
Я написал пост об этом еще в 2009 году, который вы могли бы найти полезным: http://www.leonardoborges.com/writings/2009/07/22/procs-lambdas-blocks-whats-the-difference/
Надеюсь это поможет.