为啥带有 splat 参数的 Ruby 过程/块的行为与方法和 lambda 不同?

Posted

技术标签:

【中文标题】为啥带有 splat 参数的 Ruby 过程/块的行为与方法和 lambda 不同?【英文标题】:Why do Ruby procs/blocks with splat arguments behave differently than methods and lambdas?为什么带有 splat 参数的 Ruby 过程/块的行为与方法和 lambda 不同? 【发布时间】:2014-07-19 16:17:06 【问题描述】:

为什么带有 splat 参数的 Ruby (2.0) 过程/块的行为与方法和 lambda 不同?

def foo (ids, *args)
  p ids
end
foo([1,2,3]) # => [1, 2, 3]

bar = lambda do |ids, *args|
  p ids
end
bar.call([1,2,3]) # => [1, 2, 3]

baz = proc do |ids, *args|
  p ids
end
baz.call([1,2,3]) # => 1

def qux (ids, *args)
  yield ids, *args
end
qux([1,2,3])  |ids, *args| p ids  # => 1

这是对这种行为的确认,但没有解释: http://makandracards.com/makandra/20641-careful-when-calling-a-ruby-block-with-an-array

【问题讨论】:

如果你想改进你的问题,join... 只会让它变得不必要的复杂。这与你的问题无关。您应该做的就是在每个块中执行p ids,并明确它的不同之处。 可能与proc 是标准库方法而lambda 是特殊关键字有关... @sawa 感谢您的建议! 认为你必须新建一个 Proc? ruby-doc.org/core-2.1.1/Proc.html#method-i-lambda-3F(它被称为技巧),并不是对“为什么?”的真正答案,而是一个很好的解释。 【参考方案1】:

Proc 对象有两种类型:lambda 以与普通方法相同的方式处理参数列表,proc 使用“技巧”(Proc#lambda?)。 proc 如果它是唯一的参数,则会分解一个数组,忽略额外的参数,将 nil 分配给缺少的参数。您可以使用解构部分模仿 proclambda 的行为:

->((x, y))  [x, y] [1]         #=> [1, nil]
->((x, y))  [x, y] [[1, 2]]    #=> [1, 2]
->((x, y))  [x, y] [[1, 2, 3]] #=> [1, 2]
->((x, y))  [x, y] [1, 2]      #=> ArgumentError

【讨论】:

【参考方案2】:

刚刚遇到类似的问题!

无论如何,我的主要收获:

    splat 运算符以可预测的方式进行数组分配

    过程有效地将参数分配给输入(请参阅下面的免责声明)

这会导致奇怪的行为,即上面的例子:

baz = proc do |ids, *args|
  p ids
end
baz.call([1,2,3]) # => 1

那么发生了什么? [1,2,3] 被传递给 baz,然后将数组分配给它的参数

ids, *args = [1,2,3]
ids = 1
args = [2,3]

运行时,块仅检查ids,即1。事实上,如果你将p args插入到块中,你会发现它确实是[2,3]。当然不是一个方法(或 lambda)所期望的结果。

免责声明:我不能确定 Procs 是否只是将它们的参数分配给底层的输入。但这似乎与他们不强制执行正确数量的参数的行为相匹配。事实上,如果你给一个 Proc 的参数太多,它会忽略额外的参数。太少了,它以零通过。就像变量赋值一样。

【讨论】:

哈哈,这正是我正在做的事情引发了这个问题。请参阅here 和here 了解我使用的解决方法,它基本上是将ids 包装在数组unless lambda? 中。在应用程序代码中,这种 hack 可能不是必需的,但在这里的库代码中使用了它,因为我想允许 lambda、包装的 proc 或未包装的块作为方法参数。

以上是关于为啥带有 splat 参数的 Ruby 过程/块的行为与方法和 lambda 不同?的主要内容,如果未能解决你的问题,请参考以下文章

Ruby 中的关键字参数解包(splat)

ruby 使用splat进行方法参数

Haskell 有像 Python 和 Ruby 这样的 splat 运算符吗?

如何将数组传递给接受带有 splat 运算符的属性的方法?

Ruby,Splat 的源代码?

了解范围和数组中的 ruby​​ splat