下面的 lambda 表达式如何抛出 LocalJumpError ?
我一直在阅读 Flanagan-Matz 的《Ruby 编程语言》一书
Context: Difference Between Proc.new and lambda wrt return statements
该书指出 lambda 中的 return 不应不会引发 LocalJumpError (因为 lambda类似于方法调用)。 lambda 中的 return 只是退出 lambda - 而不是包含迭代器的方法。
然而,下面的代码片段却另有说明。这里有什么问题?
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!
I've been reading the Ruby Programming Language book by Flanagan-Matz
Context: Difference between Proc.new and lambda w.r.t return statements
The book states that a return within a lambda should not raise a LocalJumpError (since lambdas are akin to method calls). A return in a lambda just exits the lambda - not the method enclosing the iterator.
However the following code-snippet says otherwise. What is the catch here?
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!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
LJE 不是由于从方法中返回,而是由于从调用块的数组迭代器返回。您无法从数组迭代器返回。通过简单地尝试从以下每个块返回来查看重现的行为:编辑:好的,我收回它,你可以从迭代器中返回。我的 LJE 是因为我在控制台(啊)。
编辑:好的,我看到你的问题了。基本问题是为什么
block[x]
有效而yield x
不起作用(假设 &block 是 lambda)。看起来yield x
内联来自 block 的解释代码并在数组迭代器的上下文中调用它,这将抛出 LJE (如上所述)和block[x ]
不会内联代码,只会从块本身返回。因此,在未能回答您的问题后,我至少将其提炼为以下内容:
所以看起来有什么不同不是在 lambda 和 Proc 之间(其行为符合预期),而是在通过
yield
调用 lambda 和通过block.call
调用 lambda 之间。调用yield
似乎会使 lambda 表现得像一个 Proc,并尝试从方法的上下文中返回。The LJE is not due to returning out of the method, but returning out of the array iterator that calls the block. You can't return from an array iterator. See the behavior reproduced by simply attempting to return from the following each block:EDIT: Ok, I take it back, you CAN return out of an iterator. My LJE was because I was in console (argh).
EDIT: Ok, I see your problem. The basic question is why does
block[x]
work andyield x
does not (assuming &block is a lambda).It would seem thatyield x
inlines the interpreted code from block and calls it in the context of the array iterator, which will throw the LJE (as above), andblock[x]
does not inline the code and only returns out of the block itself.So, having failed to answer your question, I've at least distilled it down to the following:
So the difference it would seem is not between lambda and Proc, which behaves as expected, but between calling a lambda through
yield
and calling a lambda throughblock.call
. Callingyield
appears to make the lambda behave as if it were a Proc, and attempts to return out of the context of the method.