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),则看起来组合散列与其中的第一个元素是相同的对象。这就是它们被修改的原因。当您有两个哈希 hi 并执行 **h, **i, d: 5 时,只有 h 被修改,而不是 i 还有一件事 - 如果你直接在 Rubyforum 上发帖,它不会在邮件列表中可用,而反向是可以的。所以最好把它贴在邮件列表中。我说的是当前的网关问题。 @sawa 表达式的结果与h 是同一个对象,这是一个有趣的见解,但还有更多。考虑h = a: 1 ; **h, a: 99, **h 。由于最终结果是 a: 99 ,我们可以看到,即使我们到达最终的**hh[: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 错误吗?的主要内容,如果未能解决你的问题,请参考以下文章

== 运算符和 equals() 有啥区别? (使用哈希码()???)

再看函数式编程

JavaScript 哈希表(散列表)应用

如何对网页进行哈希算法?跪求大神回答!

为啥 return 语句会破坏条件运算符?

为啥在计算哈希码时使用 xor 运算符? [复制]