Hash.each 和 lambda 之间的数量不一致
当我将以下示例放入 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>'
我了解 lambda 检查数量,但我在 lambda 定义中看到两个参数。如果我将 lambda do 更改为 Proc.new do,代码就会执行,并且我会得到预期的结果。
Josh 的示例来自 2008 年,因此我假设这是 Ruby 1.8 和 1.9 中的差异。这是怎么回事?
I lifted the following example from 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
when I put it in the the rails console (irb), and call it with a hash, I get the following:
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>'
I understand that lambdas check arity, but I see two arguments in the lambda definition. If I change lambda do
to Proc.new do
, The code executes, and I get the expected result.
Josh's example is from 2008, so I'm assuming this is a difference in Ruby 1.8 and 1.9. What's going on here?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
事实上,它似乎在 1.8 和 1.9 之间发生了变化,但这一变化在 1.9.2 上修复了它,至少在我的测试中是这样:事实
证明,这种方法也与 Ruby 1.8.7 向后兼容。
Indeed, it appears to have changed between 1.8 and 1.9, but this change fixes it for 1.9.2, at least in my tests:
This approach turns out to be backward-compatible with Ruby 1.8.7, as well.
Hash#each
与其他所有#each
方法一样,为块生成一个 参数。对于Hash#each
,该参数是一个由键和值组成的二元素数组。因此,
Hash#each
产生一个 参数,但您的 lambda 具有两个 强制参数,因此您会收到参数错误。它适用于块,因为块对其参数不太严格,特别是,如果一个块有多个参数,但只获取一个参数,它将尝试解构该参数,就像它是通过 splat 传入的一样。
Proc 有两种类型:lambda 和非 lambda(令人困惑的是,后者通常也称为 Proc)。就
return
关键字的行为方式以及(对于本例而言,更重要的是)它们如何绑定参数而言,Lambda 的行为类似于方法,而非 lambdaProc
的行为类似于中的块关于返回
和参数绑定如何工作的术语。这就是为什么Proc.new
(它创建一个非 lambdaProc
)可以工作,但lambda
(它显然创建一个 lambda)却不能。您可以通过调用
Proc#lambda?
来检查Proc
是否是 lambda。如果你想解构参数,你必须显式地这样做,就像你定义方法时一样:
而且,是的,一个更合理的块参数绑定方法,
Proc
s lambda 是 Ruby 1.9 中向后不兼容的主要变化之一。Hash#each
, just like every other#each
method, yields one argument to the block. In the case ofHash#each
, that one argument is a two-element array consisting of the key and the value.So,
Hash#each
yields one argument, but your lambda has two mandatory parameters, therefore you get an arity error.It works with blocks, since blocks are less strict about their arguments, and in particular, if a block has multiple parameters, but only gets one argument, it will try to deconstruct the argument as if it had been passed in with a splat.
There are two kinds of
Proc
s: lambdas and non-lambdas (confusingly, the latter are usually also calledProc
s). Lambdas behave like methods in terms of how thereturn
keyword behaves and (more importantly, for this case) how they bind arguments, whereas non-lambdaProc
s behave like blocks in terms of howreturn
and argument binding work. That's whyProc.new
(which creates a non-lambdaProc
) works, butlambda
(which obviously creates a lambda) doesn't.You can check whether a
Proc
is a lambda or not by callingProc#lambda?
.If you want to deconstruct the argument, you will have to do so explicitly, the same way you would when you define a method:
And, yes, a more sane approach to argument binding for blocks,
Proc
s and lambdas was one of the major backwards-incompatible changes in Ruby 1.9.