Ruby 中的关键字参数解包(splat)
Posted
技术标签:
【中文标题】Ruby 中的关键字参数解包(splat)【英文标题】:Keyword arguments unpacking (splat) in Ruby 【发布时间】:2017-08-08 11:46:18 【问题描述】:下面发生的事情对我来说似乎有点奇怪。
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)
这是编译器优化功能吗?
【问题讨论】:
好问题!我学到了很多关于关键字参数的知识。 【参考方案1】:这是一个 Ruby 的 bug,被我多次报告(例如 here 被我)但一直没有修复。
我猜自从引入了关键字参数功能后,双 splat 语法变得模糊不清,这就是这个 bug 的间接原因。我听说 Matz 正在考虑在未来的 Ruby 版本中引入一种新语法来区分哈希和关键字参数。
【讨论】:
A wrapper 和 wrappee 正是我试图遵循的模式,Sawa。我很惊讶这种非常有用的转发参数模式还没有完善。我猜是风格问题。 update:这在 ruby 2.7 中已经有所修补; ruby 3 将在较低的基础水平上解决此问题【参考方案2】:[编辑:我在完成我的工作后看到了@sawa 的回答。我是对的:这是一个错误!]
当字面空散列被双标和作为变量值的空散列被双标时,会得到不同的结果,在我看来是 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]
但是,要将双散列散列用作参数,散列不能包含未在方法定义中作为参数列出的键。
def my_method(x, a:)
[x, a]
end
h = a: 2, b: 3
my_method(1, **h)
#=> ArgumentError: unknown keyword: b
因此出现了问题:考虑到所有散列的键(无)都作为参数包含在方法定义中(在这种情况下它没有效果),是否可以将双重空散列作为参数传递?让我们试试吧。
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 或其他人应该报告它。
【讨论】:
【参考方案3】:我有一种感觉,你正在绊倒 splat 运算符与空散列结构相关的特殊性。似乎喷出一个空的内联哈希会导致它消失,但其他任何东西都会作为某种参数被扩展。
这实际上可能是 Ruby 中的一个错误,尽管这是一个非常古怪的边缘案例,我并不感到惊讶。
您的f
函数不接受任何类型的关键字参数,因此如果进行了足够有力的尝试来提供它们,它将失败。最后两个示例似乎试图强制使用空哈希作为文字参数。
【讨论】:
这不是空不空的问题。所讨论的对象是空散列文字还是指向空散列的变量很重要。而且没有理由这样做。 @sawa 我同意应该没有区别,但是有一个,这非常令人困惑。在 Ruby 中看到这种事情发生是非常不寻常的,它倾向于保持一致性。我认为这是一个错误,如果跟进,可能最终会成为 Ruby Spec 中的测试。以上是关于Ruby 中的关键字参数解包(splat)的主要内容,如果未能解决你的问题,请参考以下文章