是否保证 Ruby 哈希文字的顺序? [英] Is order of a Ruby hash literal guaranteed?

查看:28
本文介绍了是否保证 Ruby 哈希文字的顺序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Ruby 从 v1.9 开始,在循环哈希时支持确定性顺序;先添加的条目将首先返回.

这是否适用于文字,即 { a: 1, b: 2 } 总是在 b 之前产生 a?

我对 Ruby 2.1 (MRI) 进行了一个快速实验,它实际上是一致的,但是该语言在多大程度上保证可以在所有 Ruby 实现上运行?

解决方案

有几个地方可以指定这个,即一些被认为是Ruby 语言规范"的东西:

ISO 规范没有说明 Hash 排序:它的编写方式使得所有现有的 Ruby 实现都自动符合它,无需更改,即它被写入对当前的 Ruby 实现进行描述,而不是规定.在编写规范时,这些实现包括 MRI、YARV、Rubinius、JRuby、IronRuby、MagLev、MacRuby、XRuby、Ruby.NET、Cardinal、tinyrb、RubyGoLightly、SmallRuby、BlueRuby 等.特别感兴趣的是 MRI(only 实现 1.8)和 YARV(only 实现 1.9(当时)),这意味着规范只能指定行为1.8 和 1.9 通用,Hash 排序不是.

RubySpec 项目被开发人员放弃,因为 ruby​​-core 开发人员和 YARV 开发人员从未意识到它.但是,它确实(隐式地)指定Hash 字面量按从左到右的顺序:

<块引用>

new_hash(1 => 2, 4 => 8, 2 => 4).keys.should == [1, 4, 2]

这是 Hash#keys 的规范,但是,其他规范测试 Hash#valuesHash#keys 的顺序相同, Hash#each_valueHash#each_key 与它们的顺序相同,并且 Hash#each_pairHash#each> 也有相同的顺序.

我在 YARV 测试套件中找不到任何内容 指定保留排序.事实上,我在该测试套件中根本找不到任何关于订购的信息,恰恰相反:测试花了很长时间以避免取决于订购!

Flanagan/matz 书有点像在 9.5.3.6 Hash 迭代器 部分中隐式指定了 Hash 字面顺序.首先,它使用与文档大致相同的公式:

<块引用>

然而,在 Ruby 1.9 中,散列元素按其插入顺序迭代,[…]

然后它继续:

<块引用>

[…],这是以下示例中显示的顺序:

在这些例子中,它实际上使用了一个文字:

<块引用>

h = { :a=>1, :b=>2, :c=>3 }# each() 迭代器迭代 [key,value] 对h.每个{|对|打印对 } # 打印 "[:a, 1][:b, 2][:c, 3]"# 它也适用于两个块参数h.每个都做|键,值|print "#{key}:#{value} " # 打印 "a:1 b:2 c:3"结尾# 迭代键或值或两者h.each_key {|k|打印 k } # 打印abc"h.each_value {|v|print v } # 打印123"h.each_pair {|k,v|print k,v } # 打印a1b2c3".喜欢每个

他的评论中,@mu 太短提到了

<块引用>

h = { a: 1, b: 2 } 等同于 h = { };h[:a] = 1;h[:b] = 2

另一条评论中><块引用>

没有任何意义

不幸的是,事实并非如此:

模块 HashASETWithLoggingdef []=(键,值)puts "[]= 被调用 [#{key.inspect}] = #{value.inspect}"极好的结尾结尾类哈希前置 HashASETWithLogging结尾h = { a: 1, b: 2 }# 什么都不打印h = { };h[:a] = 1;h[:b] = 2# []= 是用 [:a] = 1 调用的# []= 是用 [:b] = 2 调用的

因此,根据您如何解释书中的那句话以及根据您判断该书的规范化"程度,是的,文字的顺序有保证的.

Ruby, since v1.9, supports a deterministic order when looping through a hash; entries added first will be returned first.

Does this apply to literals, i.e. will { a: 1, b: 2 } always yield a before b?

I did a quick experiment with Ruby 2.1 (MRI) and it was in fact consistent, but to what extent is this guaranteed by the language to work on all Ruby implementations?

解决方案

There are couple of locations where this could be specified, i.e. a couple of things that are considered "The Ruby Language Specification":

The ISO spec doesn't say anything about Hash ordering: it was written in such a way that all existing Ruby implementations are automatically compliant with it, without having to change, i.e. it was written to be descriptive of current Ruby implementations, not prescriptive. At the time the spec was written, those implementations included MRI, YARV, Rubinius, JRuby, IronRuby, MagLev, MacRuby, XRuby, Ruby.NET, Cardinal, tinyrb, RubyGoLightly, SmallRuby, BlueRuby, and others. Of particular interest are MRI (which only implements 1.8) and YARV (which only implements 1.9 (at the time)), which means that the spec can only specify behavior which is common to 1.8 and 1.9, which Hash ordering is not.

The RubySpec project was abandoned by its developers out of frustration that the ruby-core developers and YARV developers never recognized it. It does, however, (implicitly) specify that Hash literals are ordered left-to-right:

new_hash(1 => 2, 4 => 8, 2 => 4).keys.should == [1, 4, 2]

That's the spec for Hash#keys, however, the other specs test that Hash#values has the same order as Hash#keys, Hash#each_value and Hash#each_key has the same order as those, and Hash#each_pair and Hash#each have the same order as well.

I couldn't find anything in the YARV testsuite that specifies that ordering is preserved. In fact, I couldn't find anything at all about ordering in that testsuite, quite the opposite: the tests go to great length to avoid depending on ordering!

The Flanagan/matz book kinda-sorta implicitly specifies Hash literal ordering in section 9.5.3.6 Hash iterators. First, it uses much the same formulation as the docs:

In Ruby 1.9, however, hash elements are iterated in their insertion order, […]

But then it goes on:

[…], and that is the order shown in the following examples:

And in those examples, it actually uses a literal:

h = { :a=>1, :b=>2, :c=>3 }

# The each() iterator iterates [key,value] pairs
h.each {|pair| print pair }    # Prints "[:a, 1][:b, 2][:c, 3]"

# It also works with two block arguments
h.each do |key, value|                
  print "#{key}:#{value} "     # Prints "a:1 b:2 c:3" 
end

# Iterate over keys or values or both
h.each_key {|k| print k }      # Prints "abc"
h.each_value {|v| print v }    # Prints "123"
h.each_pair {|k,v| print k,v } # Prints "a1b2c3". Like each

In his comment, @mu is too short mentioned that

h = { a: 1, b: 2 } is the same as h = { }; h[:a] = 1; h[:b] = 2

and in another comment that

nothing else would make any sense

Unfortunately, that is not true:

module HashASETWithLogging
  def []=(key, value)
    puts "[]= was called with [#{key.inspect}] = #{value.inspect}"
    super
  end
end

class Hash
  prepend HashASETWithLogging
end

h = { a: 1, b: 2 }
# prints nothing

h = { }; h[:a] = 1; h[:b] = 2
# []= was called with [:a] = 1
# []= was called with [:b] = 2

So, depending on how you interpret that line from the book and depending on how "specification-ish" you judge that book, yes, ordering of literals is guaranteed.

这篇关于是否保证 Ruby 哈希文字的顺序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆