Hash.each和lambdas之间的arity不一致

时间:2011-03-10 16:32:49

标签: ruby lambda closures proc-object

我从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 do更改为Proc.new do,则会执行代码,并获得预期结果。

Josh的例子是从2008年开始的,所以我假设这是Ruby 1.8和1.9的不同之处。这是怎么回事?

2 个答案:

答案 0 :(得分: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。

答案 1 :(得分:1)

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

因此,Hash#each会产生一个参数,但您的lambda有两个必需参数,因此会出现arity错误。

它适用于块,因为块对它们的参数不太严格,特别是,如果一个块有多个参数,但只获得一个参数,它将尝试解析该参数,就像它已经传入一个参数一样图示。

有两种Proc s:lambdas和non-lambdas(令人困惑的是,后者通常也被称为Proc s)。 Lambdas在return关键字的行为方式和(更重要的是,对于这种情况)如何绑定参数方面表现得像方法,而非lambda Proc在行为方面表现得像{{1和参数绑定工作。这就是为什么return(创建非lambda Proc.new)有效的原因,但是Proc(显然创建了一个lambda)却没有。

您可以致电lambda来检查Proc是否为lambda。

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

Proc#lambda?

而且,是的,对于块,lambda do |(key, value)| 和lambdas 的参数绑定更为理智的方法是 Ruby 1.9中主要向后兼容的变化之一。