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

问题描述:

下面发生的事情对我来说似乎有点奇怪。关键字参数在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) 

这是编译器优化功能吗?

+0

好问题!我学到了很多关于关键字的论点。 –

这是一个多次报告的Ruby错误(例如我的here),但尚未修复。

我猜想,由于引入了关键字参数功能,双重splat语法变得模糊,这就是这个错误的间接原因。我听说Matz正在考虑在未来的Ruby版本中引入新的语法来区分散列和关键字参数。

+0

包装和包装正是我想要遵循的模式,Sawa。我很惊讶这种非常有用的转发参数模式尚未完善。我想,风格问题。 –

我有一种感觉,你正在绊倒与该空的散列结构相关的摔跤运算符的特性。看起来,一个空的内联哈希引起它消失,但其他任何东西都会扩展为某种类型的参数。

这实际上可能是Ruby中的一个错误,虽然这是一个古怪的边缘案例,我并不感到惊讶。

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

+0

这不是空的问题。所讨论的对象是空的哈希文字还是指向空哈希的变量。没有理由这样做。 – sawa

+1

@sawa我同意不应该有任何区别,但是有一个,这是非常令人困惑的。在Ruby中看到这种事情是非常罕见的,它趋向于一致性。我认为这是一个错误,如果继续下去,最终可能会成为Ruby Spec的测试。 – tadman

[编辑:我看到@ sawa完成我的回答。我是对的:这是一个bug!]

当字面空的散列是双重splatted并且空的散列是变量的值是双重splatted时,得到不同的结果,在我看来似乎是prima facia证明这是由于Ruby中的错误。为了理解错误存在的原因,首先考虑将双重散列哈希传递给方法的原因。

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

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"] 

现在让我们使用双摔散列哈希。

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) 

不!

这没有任何意义。所以假设这是一个错误,它怎么会出现?正如OP建议的那样,我怀疑它可能与Ruby的优化有关。它的空散列是一个文字,在Ruby的代码中可以比之前处理变量的值更早处理它。我猜测,无论谁写了早期的代码,对上面提出的问题都回答“是”,而编写后者代码的人回答“否”,或者没有考虑当时的空散列情况。

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