3

我从Josh Susser举起以下示例

  def strip_accents params
    thunk = lambda do |key,value|
      case value
        when String then value.remove_accents!
        when Hash   then value.each(&thunk)
      end
    end
    params.each(&thunk)
  end

当我把它放在 Rails 控制台(irb)中,并用哈希调用它时,我得到以下信息:

ruby-1.9.2-p136 :044 > `ruby --version`
 => "ruby 1.9.2p136 (2010-12-25 revision 30365) [i686-linux]\n"
ruby-1.9.2-p136 :045 > strip_accents({:packs=>{:qty=>1}})
ArgumentError: wrong number of arguments (1 for 2)
        from (irb):32:in `block in strip_accents'
        from (irb):37:in `each'
        from (irb):37:in `strip_accents'
        from (irb):45
        from /longpathtrimedforclarity/console.rb:44:in `start'
        from /longpathtrimedforclarity/console.rb:8:in `start'
        from /longpathtrimedforclarity/commands.rb:23:in `<top (required)>'
        from script/rails:6:in `require'
        from script/rails:6:in `<main>'

我知道 lambdas 检查 arity,但我在 lambda 定义中看到了两个参数。如果我更改lambda doProc.new do,代码会执行,我会得到预期的结果。

Josh 的示例来自 2008 年,所以我假设这是 Ruby 1.8 和 1.9 的差异。这里发生了什么?

4

2 回答 2

3

事实上,它似乎在 1.8 和 1.9 之间发生了变化,但这个变化修复了 1.9.2,至少在我的测试中:

def strip_accents params
  thunk = lambda do |h|
    key, value = h
    case value
    when String then value.remove_accents!
    when Hash   then value.each(&thunk)
    end
  end
  params.each(&thunk)
end

事实证明,这种方法也向后兼容 Ruby 1.8.7。

于 2011-03-10T16:56:33.090 回答
1

Hash#each,就像其他所有#each方法一样,为块产生一个参数。在 的情况下Hash#each,一个参数是一个由键和值组成的二元素数组。

所以,Hash#each产生一个参数,但你的 lambda 有两个强制参数,因此你得到一个 arity 错误。

它适用于块,因为块对它们的参数不那么严格,特别是,如果一个块有多个参数,但只有一个参数,它会尝试解构参数,就好像它是用 splat 传入的一样。

s有两种Proc:lambdas 和 non-lambdas(令人困惑的是,后者通常也称为Procs)。就关键字的行为方式和(更重要的是,对于这种情况而言)它们如何绑定参数而言,Lambda 的行为类似于方法return,而非 lambda在如何和参数绑定的工作Proc方式方面的行为类似于块。return这就是为什么Proc.new(创建一个非 lambda Proc)有效,但lambda(显然创建一个 lambda)没有。

您可以Proc通过调用来检查 a 是否为 lambda Proc#lambda?

如果你想解构参数,你必须明确地这样做,就像你定义一个方法时一样:

lambda do |(key, value)|

而且,是的,对块、Procs 和 lambdas进行参数绑定的更明智的方法是Ruby 1.9 中主要的向后不兼容的更改之一。

于 2011-03-10T22:37:15.467 回答