为啥带有 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
分配给缺少的参数。您可以使用解构部分模仿 proc
和 lambda
的行为:
->((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 不同?的主要内容,如果未能解决你的问题,请参考以下文章