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

下面发生的事情对我来说似乎有点奇怪。

def f(a, b) puts "#{a} :: #{b}" end f(*[1, 2], **{}) # prints "1 :: 2" hash = {} f(*[1, 2], **hash) ArgumentError: wrong number of arguments (3 for 2) f(*[1, 2], **Hash.new) ArgumentError: wrong number of arguments (3 for 2) 

这是编译器优化function吗?

这是一个Ruby的错误已被多次报告(例如我在这里 )但尚未修复。

我想,由于引入了关键字参数function,双splat语法变得模糊,这就是这个bug的间接原因。 我听说Matz正在考虑在未来的Ruby版本中引入一种新语法来区分哈希和关键字参数。

[编辑:我完成了我之后看到了@ sawa的回答。 我是对的:这是一个错误!]

当文字空哈希被双重绘制并且空哈希值是变量的值被双重打印时,获得了不同的结果,在我看来,这是由于Ruby中的错误导致的初步证据。 要了解错误可能存在的原因,请首先考虑将双splatted哈希传递给方法的原因。

假设我们使用一些关键字参数定义一个方法:

 def my_method(x, a: 'cat', b: 'dog') [x, a, b] end my_method(1) #=> [1, "cat", "dog"] 

默认值适用于两个关键字参数。 现在尝试:

 my_method(1, a: 2) #=> [1, 2, "dog"] 

现在让我们使用双splatted哈希。

 h = { a: 2, b: 3 } my_method(1, **h) #=> [1, 2, 3] 

这与所需的关键字参数(Ruby 2.1+)相同。

 def my_method(x, a:, b:) [x, a, b] end my_method(1, **h) #=> [1, 2, 3] 

但是,要使用双splatted散列作为参数,散列不能包含未在方法定义中作为参数列出的键。

 def my_method(x, a:) [x, a] end h = { a: 2, b: 3 } my_method(1, **h) #=> ArgumentError: unknown keyword: b 

因此出现了这样的问题:考虑到所有哈希的键(无)都作为参数包含在方法定义中(这种情况下它没有效果),可以将双splatted空哈希作为参数传递吗? 我们来试试吧。

 def my_method(x) [x] end my_method(1, **{}) #=> [1] 

是!

 h = {} my_method(1, **h) #=> ArgumentError: wrong number of arguments (given 2, expected 1) 

没有!

这是没有意义的。 所以假设这是一个错误,它怎么可能出现? 我怀疑它可能与Ruby的优化有关,正如OP所建议的那样。 空哈希是一个文字,可以在Ruby的代码中更早地处理它,而不是它是变量的值。 我猜这个编写前面代码的人对我上面提出的问题回答“是”,并且编写后一个代码的人回答“否”,或者没有考虑那个时候空哈希的情况。

如果这个错误理论没有被击落,OP或其他人应该报告它。

我有一种感觉,你正在绊倒splat运算符与空哈希结构相关的特性。 splatting一个空的内联哈希似乎会使它消失,但其他任何东西都会被扩展为某种类型的参数。

事实上,这可能是Ruby中的一个错误,虽然它是如此古怪的边缘情况,我并不感到惊讶。

你的f函数不接受任何类型的关键字参数,所以如果有足够的力量尝试提供它们就会失败。 最后两个例子似乎试图强制将空哈希作为文字参数。