Double-splat 运算符破坏性地修改了哈希——这是一个 Ruby 错误吗?
Posted
技术标签:
【中文标题】Double-splat 运算符破坏性地修改了哈希——这是一个 Ruby 错误吗?【英文标题】:Double-splat operator destructively modifies hash – is this a Ruby bug? 【发布时间】:2014-06-10 12:45:52 【问题描述】:我注意到我发现 Ruby 2.1.1 中的 **
(double-splat) 运算符有一个非常令人惊讶的行为。
当在**hash
之前使用键值对时,哈希值保持不变;但是,当键值对仅在**hash
之后使用时,哈希值会被永久修改。
h = b: 2
a: 1, **h # => a: 1, b: 2
h # => b: 2
a: 1, **h, c: 3 # => a: 1, b: 2, c: 3
h # => b: 2
**h, c: 3 # => b: 2, c: 3
h # => b: 2, c: 3
为了比较,请考虑数组上的单*
运算符的行为:
a = [2]
[1, *a] # => [1, 2]
a # => [2]
[1, *a, 3] # => [1, 2, 3]
a # => [2]
[*a, 3] # => [2, 3]
a # => [2]
数组始终保持不变。
我们认为**
有时具有破坏性的行为是故意的,还是看起来更像是一个错误?
无论哪种情况,描述**
运算符如何工作的文档在哪里?
我也问过这个问题in the Ruby Forum。
更新
该错误已在 Ruby 2.1.3+ 中修复。
【问题讨论】:
参数列表中的使用在核心文档ruby-doc.org/core-2.1.1/doc/syntax/methods_rdoc.html中。散列和数组文字插值似乎没有出现在任何地方,尽管单次口角至少有一个规范:github.com/rubyspec/rubyspec/blob/master/language/splat_spec.rb。双拍没有类似的东西。 Ruby 语义似乎确实是民间传说。我确信这是一个错误,因为未记录的语言功能可能是错误的! 我什至不知道您可以在无方法签名中使用它... 如果组合散列是散列(它们具有相同的对象 id),则看起来组合散列与其中的第一个元素是相同的对象。这就是它们被修改的原因。当您有两个哈希h
和 i
并执行 **h, **i, d: 5
时,只有 h
被修改,而不是 i
。
还有一件事 - 如果你直接在 Rubyforum 上发帖,它不会在邮件列表中可用,而反向是可以的。所以最好把它贴在邮件列表中。我说的是当前的网关问题。
@sawa 表达式的结果与h
是同一个对象,这是一个有趣的见解,但还有更多。考虑h = a: 1 ; **h, a: 99, **h
。由于最终结果是 a: 99
,我们可以看到,即使我们到达最终的**h
,h[:a]
已经被覆盖了。
【参考方案1】:
问题的答案似乎是:
这可能是一个错误,而不是故意的。
**
运算符的行为在 core library rdoc 中有非常简短的记录。
感谢几位评论者的建议,我已将该错误发布到Ruby trunk issue tracker。
更新:
该错误已在changeset r45724 中修复。那里的评论是“keyword splat 应该是非破坏性的”,这使得这是一个权威的答案。
【讨论】:
该错误似乎已经是fixed。很快。 @sawa 它很快,但需要说它看起来像一个 hack,然后是一个修复。只是用哈希的浅拷贝覆盖先前调用的指针,省略完整条件。感觉有点尴尬。【参考方案2】:我注意到 2.1.5 和 2.3.1 之间的差异
例子是一个irb方法和调用它的方式
$ irb
>> def foo(opts) opts end
=> :foo
>> foo a: 'a', ** a: 'b'
在 2.1.5 中,以下导致保留值
=> :a=>"a"
在 2.3.1 中,值为 'b'
(irb):2: warning: duplicated key at line 2 ignored: :a
=> :a=>"b"
我不确定应该是哪个?
在 2.3.1 中,作为双 splat 提供的哈希会覆盖列表中第一项的相同键。
【讨论】:
以上是关于Double-splat 运算符破坏性地修改了哈希——这是一个 Ruby 错误吗?的主要内容,如果未能解决你的问题,请参考以下文章