Hash.each 和 lambda 之间的数量不一致

发布于 2024-10-21 17:43:34 字数 1269 浏览 14 评论 0原文

当我将以下示例放入 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

離殇 2024-10-28 17:43:34

事实上,它似乎在 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 向后兼容。

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:

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

This approach turns out to be backward-compatible with Ruby 1.8.7, as well.

旧情别恋 2024-10-28 17:43:34

Hash#each 与其他所有 #each 方法一样,为块生成一个 参数。对于Hash#each,该参数是一个由键和值组成的二元素数组。

因此,Hash#each 产生一个 参数,但您的 lambda 具有两个 强制参数,因此您会收到参数错误。

它适用于块,因为块对其参数不太严格,特别是,如果一个块有多个参数,但只获取一个参数,它将尝试解构该参数,就像它是通过 splat 传入的一样。

Proc 有两种类型:lambda 和非 lambda(令人困惑的是,后者通常也称为 Proc)。就 return 关键字的行为方式以及(对于本例而言,更重要的是)它们如何绑定参数而言,Lambda 的行为类似于方法,而非 lambda Proc 的行为类似于中的块关于返回和参数绑定如何工作的术语。这就是为什么 Proc.new (它创建一个非 lambda Proc)可以工作,但 lambda (它显然创建一个 lambda)却不能。

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

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

lambda do |(key, value)|

而且,是的,一个更合理的块参数绑定方法,Procs lambda 是 Ruby 1.9 中向后不兼容的主要变化之一。

Hash#each, just like every other #each method, yields one argument to the block. In the case of Hash#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 Procs: lambdas and non-lambdas (confusingly, the latter are usually also called Procs). Lambdas behave like methods in terms of how the return keyword behaves and (more importantly, for this case) how they bind arguments, whereas non-lambda Procs behave like blocks in terms of how return and argument binding work. That's why Proc.new (which creates a non-lambda Proc) works, but lambda (which obviously creates a lambda) doesn't.

You can check whether a Proc is a lambda or not by calling Proc#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:

lambda do |(key, value)|

And, yes, a more sane approach to argument binding for blocks, Procs and lambdas was one of the major backwards-incompatible changes in Ruby 1.9.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文