2

我一直在阅读 Flanagan-Matz 的 Ruby Programming Language 一书

上下文:Proc.new 和 lambda wrt return 语句之间的区别

该书指出,lambda 中的返回不应引发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!
4

1 回答 1

4

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

[1,2,3].每个 {|x| 返回 x } LocalJumpError: 从 (irb):7 意外返回 (irb):7:in `each' from (irb):7

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

编辑:好的,我看到了你的问题。基本问题是为什么block[x]有效而yield x无效(假设 &block 是一个 lambda)。似乎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

因此,看起来的区别不在于 lambda 和 Proc 之间,后者的行为与预期的一样,而是在调用 lambda throughyield和调用 lambda through之间block.call。调用yield似乎使 lambda 表现得好像它是一个 Proc,并尝试从方法的上下文中返回。

于 2010-02-08T15:38:18.883 回答