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)

这是编译器优化功能吗?

这是一个 Ruby 的错误,已经报告了好几次(例如我在这里),但尚未修复。

我猜自从引入了关键字参数功能以来,双 splat 语法变得模糊,这就是此错误的间接原因。我听说Matz正在考虑在未来的Ruby版本中引入一种新的语法来区分哈希和关键字参数。

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

当一个字面的空哈希被双拼接而一个作为变量值的空散列被双拼接时,会得到不同的结果,在我看来,这是由于 Ruby 中的一个错误。若要了解 bug 可能存在的原因,请首先考虑将双散列哈希传递给方法的原因。

假设我们定义了一个带有一些关键字参数的方法:

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]

但是,若要使用双拆分哈希作为参数,哈希不能包含未在方法定义中列为参数的键。

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

因此出现了一个问题:考虑到所有哈希的键(none)都作为参数包含在方法定义中(在这种情况下它不会产生任何影响),是否可以将双散装空哈希作为参数传递?让我们试试吧。

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或其他人应该报告它。

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

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

您的f函数不接受任何类型的关键字参数,因此如果进行了足够有力的尝试来提供它们,它将失败。最后两个示例似乎试图强制输入空哈希作为文字参数。

最新更新