Почему оператор break в ruby ​​ведет себя по-разному при использовании Proc.new v. Знак амперсанда? - PullRequest
7 голосов
/ 17 января 2012

Оператор 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 ]

7 голосов
/ 25 января 2012

break делает блок и вызывающей стороной возврата блока.В следующем коде:

proc = Proc.new { break }

"Вызывающим" блоком, который преобразуется в объект Proc, является Proc.new.break должен сделать так, чтобы вызывающий блок возвращался, но Proc.new уже вернулся.

В этом коде:

def iterator(&b); b.call; end
iterator { break }

Вызывающий блок - iteratorтак что iterator возвращается.

3 голосов
/ 17 января 2012

Вот ответ .

Амперсанд используется для преобразования процедуры в блок, а блока в процедуру.

Я изменил пример, чтобы связатьна ваш случай:

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
0 голосов
/ 17 января 2012

Это связано с разницей между блоками, процессами и лямбдами - и их соответствующими областями действия.

Я написал об этом пост еще в 2009 году, который может оказаться полезным: http://www.leonardoborges.com/writings/2009/07/22/procs-lambdas-blocks-whats-the-difference/

Надеюсь, это поможет.

...