Как LocalJumpError выбрасывается из следующей лямбды? - PullRequest
2 голосов
/ 08 февраля 2010

Я читал книгу по языку программирования Ruby Фланагана-Матца

Контекст: Разница между Proc.new и лямбда-оператором возврата w.r.t

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

Однако следующий фрагмент кода говорит об обратном. В чем тут подвох?

def caller( aProc, switch)
  puts "Start Caller"
  case switch
    when :normal_block; iterator &aProc
    when :block_parameter; iterator_blockparam(&aProc)
    else iterator_param(aProc)
  end
  puts "End Caller"
end

def iterator
  puts "Begin iterator"
  [1,2,3,4].each { |x| yield x  }
  puts "End iterator"
end
def iterator_blockparam(&block)
  puts "Start Iterator wBlockParam"
  puts "Block is {block.lambda? ? 'Lambda' : 'Proc'}"
  [1,2,3,4].each { |x| block[x]  }
  puts "End Iterator wBlockParam"
end
def iterator_param(aProc)
  puts "Start Iterator wParam"
  puts "Block is {aProc.lambda? ? 'Lambda' : 'Proc'}"
  [1,2,3,4].each{ |x| aProc[x]}
  puts "End Iterator wParam"
end
 # enclosing method Proc.new already returned.
no3proc = Proc.new{|x| return -101 if x == 3; puts x }
no3lambda = lambda{|x| return -101 if x == 3; puts x }

#~ caller( no3proc, :normal_block)         #=> LocalJumpError
caller( no3lambda, :normal_block )       #  => LJE
#~ caller( no3proc, :block_parameter)        #=> LJE
#~ caller( no3lambda, :block_parameter)       # works!
#~ caller(no3proc, :with_param)               #=> LJE
#~ caller(no3lambda, :with_param)              # works!

1 Ответ

4 голосов
/ 08 февраля 2010

LJE не из-за возврата из метода, а из-за итератора массива, который вызывает блок. Вы не можете вернуться из итератора массива. Посмотрите, как воспроизводится поведение, просто пытаясь вернуться из следующего блока:

[1,2,3] .each {| x | возврат х} LocalJumpError: неожиданное возвращение из (irb): 7 из (irb): 7: в "каждом" из (irb): 7

РЕДАКТИРОВАТЬ: Хорошо, я забираю это обратно, вы можете вернуться из итератора. Мой ЖЖ был потому, что я был в консоли (аааа).

РЕДАКТИРОВАТЬ: Хорошо, я вижу вашу проблему. Основной вопрос заключается в том, почему block[x] работает, а yield x - нет (при условии, что & block - лямбда). Казалось бы, yield x вставляет интерпретируемый код из блока и вызывает его в контексте итератора массива, который сгенерирует LJE (как указано выше), а block[x] не вставляет код в код и только возвращает его самого блока.

Итак, не ответив на ваш вопрос, я по крайней мере перевел его на следующее:

def call_as_block(&block)
    block.call
end

def call_as_yield
    yield
end

proc_return = Proc.new { return }
lambda_return = lambda { return }

call_as_block &proc_return # throws LJE
call_as_yield &proc_return # throws LJE
call_as_block &lambda_return # does NOT throw LJE
call_as_yield &lambda_return # throws LJE

Таким образом, разница, по-видимому, заключается не в лямбде и Proc, которые ведут себя, как ожидается, а в том, что между вызовом лямбды через yield и вызовом лямбды через block.call. Вызов yield, по-видимому, заставляет лямбду вести себя так, как будто это Proc, и пытается вернуться из контекста метода.

...