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

我从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 `' from script/rails:6:in `require' from script/rails:6:in `' 

我理解lambdas检查arity,但我在lambda定义中看到两个参数。 如果我将lambda do更改为Proc.new do ,则代码执行,我得到预期的结果。

Josh的例子是从2008年开始的,所以我假设这是Ruby 1.8和1.9的不同之处。 这里发生了什么?

实际上,它似乎已经在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。

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

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

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

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

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

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

 lambda do |(key, value)| 

并且,是的,对于块, Proc和lambdas的参数绑定更为理智的方法 Ruby 1.9中主要的向后不兼容的变化之一。