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

时间:2021-09-28 22:30:58

What is happening below seems a little strange to me.

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

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)

Is this a compiler optimization feature?

这是编译器优化功能吗?

3 个解决方案

#1


5  

That is a Ruby's bug that has been reported several times (for example here by me) but has not been fixed.

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

I guess that since the keyword argument feature has been introduced, the double splat syntax has become murky, and that is the indirect cause of this bug. I heard that Matz is considering of introducing a new syntax in some future version of Ruby to distinguish hashes and keyword arguments.

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

#2


2  

[Edit: I saw @sawa's answer after completing mine. I was right: it's a bug!]

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

That different results are obtained when a literal empty hash is double-splatted and an empty hash that is the value of a variable is double-splatted, seems to me to be prima facia evidence that it's due to a bug in Ruby. To understand why the bug may exist, consider first the reason for passing a double-splatted hash to a method.

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

Suppose we define a method with some keyword arguments:

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

def my_method(x, a: 'cat', b: 'dog')
  [x, a, b]
end

my_method(1)
  #=> [1, "cat", "dog"] 

The defaults apply for the two keyword arguments. Now try:

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

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

Now lets use a double-splatted hash.

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

h = { a: 2, b: 3 }

my_method(1, **h)
 #=> [1, 2, 3] 

This works the same with required keyword arguments (Ruby 2.1+).

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

def my_method(x, a:, b:)
  [x, a, b]
end

my_method(1, **h)
  #=> [1, 2, 3]

However, to use a double-splatted hash as an argument, the hash cannot contain keys that are not listed as arguments in the method definition.

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

def my_method(x, a:)
  [x, a]
end

h = { a: 2, b: 3 }

my_method(1, **h)
  #=> ArgumentError: unknown keyword: b

The question therefore arises: can a double-splatted empty hash be passed as an argument, considering that all the hash's keys (none) are included as arguments in the method definition (which case it would have no effect)? Let's try it.

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

def my_method(x)
  [x]
end

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

Yes!

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

No!

That makes no sense. So assuming this is a bug, how might it have arisen? I suspect it may have to do with an optimization of Ruby, as suggested by the OP. It the empty hash is a literal, one could deal with it earlier in Ruby's code than if it were the value of variable. I'm guessing that whoever wrote the earlier code answered "yes" to the question I posed above, and whoever wrote the latter code answered "no", or failed to consider the case of an empty hash at that point.

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

If this bug theory is not shot down, the OP or someone else should report it.

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

#3


1  

I have a feeling you're tripping over a peculiarity of the splat operator in relation to that empty hash structure. It seems that splatting an empty inline hash causes it to disappear, but anything else gets expanded as an argument of some sort.

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

This may in fact be a bug in Ruby, though it's such a quirky edge case I'm not really surprised.

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

Your f function does not accept keyword arguments of any sort, so if a sufficiently vigorous attempt is made to supply them it'll fail out. The last two examples seem to be trying to force in an empty hash as a literal argument.

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

#1


5  

That is a Ruby's bug that has been reported several times (for example here by me) but has not been fixed.

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

I guess that since the keyword argument feature has been introduced, the double splat syntax has become murky, and that is the indirect cause of this bug. I heard that Matz is considering of introducing a new syntax in some future version of Ruby to distinguish hashes and keyword arguments.

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

#2


2  

[Edit: I saw @sawa's answer after completing mine. I was right: it's a bug!]

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

That different results are obtained when a literal empty hash is double-splatted and an empty hash that is the value of a variable is double-splatted, seems to me to be prima facia evidence that it's due to a bug in Ruby. To understand why the bug may exist, consider first the reason for passing a double-splatted hash to a method.

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

Suppose we define a method with some keyword arguments:

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

def my_method(x, a: 'cat', b: 'dog')
  [x, a, b]
end

my_method(1)
  #=> [1, "cat", "dog"] 

The defaults apply for the two keyword arguments. Now try:

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

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

Now lets use a double-splatted hash.

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

h = { a: 2, b: 3 }

my_method(1, **h)
 #=> [1, 2, 3] 

This works the same with required keyword arguments (Ruby 2.1+).

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

def my_method(x, a:, b:)
  [x, a, b]
end

my_method(1, **h)
  #=> [1, 2, 3]

However, to use a double-splatted hash as an argument, the hash cannot contain keys that are not listed as arguments in the method definition.

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

def my_method(x, a:)
  [x, a]
end

h = { a: 2, b: 3 }

my_method(1, **h)
  #=> ArgumentError: unknown keyword: b

The question therefore arises: can a double-splatted empty hash be passed as an argument, considering that all the hash's keys (none) are included as arguments in the method definition (which case it would have no effect)? Let's try it.

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

def my_method(x)
  [x]
end

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

Yes!

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

No!

That makes no sense. So assuming this is a bug, how might it have arisen? I suspect it may have to do with an optimization of Ruby, as suggested by the OP. It the empty hash is a literal, one could deal with it earlier in Ruby's code than if it were the value of variable. I'm guessing that whoever wrote the earlier code answered "yes" to the question I posed above, and whoever wrote the latter code answered "no", or failed to consider the case of an empty hash at that point.

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

If this bug theory is not shot down, the OP or someone else should report it.

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

#3


1  

I have a feeling you're tripping over a peculiarity of the splat operator in relation to that empty hash structure. It seems that splatting an empty inline hash causes it to disappear, but anything else gets expanded as an argument of some sort.

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

This may in fact be a bug in Ruby, though it's such a quirky edge case I'm not really surprised.

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

Your f function does not accept keyword arguments of any sort, so if a sufficiently vigorous attempt is made to supply them it'll fail out. The last two examples seem to be trying to force in an empty hash as a literal argument.

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