下面的 lambda 表达式如何抛出 LocalJumpError ?

发布于 2024-08-20 19:51:07 字数 1513 浏览 4 评论 0原文

我一直在阅读 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

↙温凉少女 2024-08-27 19:51:07

LJE 不是由于从方法中返回,而是由于从调用块的数组迭代器返回。您无法从数组迭代器返回。通过简单地尝试从以下每个块返回来查看重现的行为:


<块引用><罢工>

[1,2,3].each {|x|返回 x }
LocalJumpError:意外返回
来自(irb):7
来自(irb):7:在“每个”中
来自(irb):7

编辑:好的,我收回它,你可以从迭代器中返回。我的 LJE 是因为我在控制台(啊)。

编辑:好的,我看到你的问题了。基本问题是为什么 block[x] 有效而 yield x 不起作用(假设 &block 是 lambda)。 看起来 yield x 内联来自 block 的解释代码并在数组迭代器的上下文中调用它,这将抛出 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

所以看起来有什么不同不是在 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:

[1,2,3].each {|x| return x }
LocalJumpError: unexpected return
from (irb):7
from (irb):7:in `each'
from (irb):7

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 and yield x does not (assuming &block is a lambda). It would seem that yield x inlines the interpreted code from block and calls it in the context of the array iterator, which will throw the LJE (as above), and block[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:

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

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 through block.call. Calling yield appears to make the lambda behave as if it were a Proc, and attempts to return out of the context of the method.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文