当子元素不是数组时,在splat参数之后需要可选的关键字参数

时间:2019-03-13 23:37:18

标签: ruby

根据此post,可以在splat参数之后有一个可选的关键字参数。如果splat参数引入了一个数组数组,则该方法有效,但是当它是一个哈希数组时,则无效

例如,如果被调用的方法定义为

def call(*scores, alpha: nil)
  puts scores
end

然后这有效

scores = [[1,2],[3,4]]
call(*scores)

但这不是

scores = [ {a: 1}, {b: 3}]
call(*scores)

给出以下内容(使用ruby 2.4.4)

ArgumentError: unknown keyword: b

但这可行

scores = [ {a: 1}, {b: 3}]
call(*scores, alpha: nil)

这是怎么回事?

2 个答案:

答案 0 :(得分:3)

splat运算符将数组拆分为参数。

但是,如果将其包装在数组中,它将再次起作用,但是现在它是数组中的一个数组,并且仍然被视为传递给您的方法的单个参数。

call([*scores]) #no error

还要说明一下为什么会出现错误,请看这里发生的情况:

def call(*scores, alpha: nil)
  puts scores.inspect
end

call(*scores[0]) #=> #[[:a, 1]]

更新:感谢@Stefan,导致错误的原因实际上是您的方法接受关键字参数,这显然是一个已知的错误。参见Keyword arguments unpacking (splat) in Ruby

最后一个示例起作用的原因是,通过将第二个参数传递给方法,splat会将第一个参数作为数组处理,而不是尝试将其拆分为2个参数。

更多信息,请参见Ruby, Source Code of Splat?

另请参见https://www.rubyguides.com/2018/07/ruby-operators/#Ruby_Splat_Operator

答案 1 :(得分:2)

*将数组元素转换为参数列表,因此:

call(*[{a: 1}, {b: 3}])

等效于:

call({a: 1}, {b: 3})

Ruby还将隐式地将散列转换为关键字参数(没有**),因此上面的代码等效于:

call({a: 1}, b: 3)

因此,{a: 1}被视为位置参数,而b: 3(或{b: 3})被视为关键字参数。而且由于call不使用名为b的关键字参数,所以您得到ArgumentError: unknown keyword: b

为避免这种情况,您可以传递一个额外的空哈希作为最后一个参数:

call({a:1}, {b:2}, {})

或:

call(*[{a:1}, {b:2}], {})

scores = [{a:1}, {b:2}]
call(*scores, {})

有一个feature request在Ruby 3中添加“真实”关键字参数。


IMO,使用call(*scores, **{})表示“无关键字参数”会更正确,但是由于bug,目前无法使用。但是,您可以使用call(*scores, **Hash.new)