Гарантирован ли порядок литерала Ruby хеша?

Ruby, начиная с v1.9, поддерживает детерминированный порядок при циклическом прохождении хеша; Записи, добавленные первыми, будут возвращены первыми.

Относится ли это к литералам, т.е. { a: 1, b: 2 } всегда приносить до б?

Я провел быстрый эксперимент с Ruby 2.1 (MRI), и он действительно был непротиворечивым, но в какой степени это гарантировано языком для работы на всех реализациях Ruby?

2 ответа

Решение

Есть несколько мест, где это может быть указано, т.е. пара вещей, которые считаются "Спецификацией языка Ruby":

Спецификация ISO ничего не говорит о Hash упорядочение: он был написан таким образом, что все существующие реализации Ruby автоматически соответствуют ему, без необходимости изменения, т.е. он был написан для описания текущих реализаций Ruby, а не для предписания. На момент написания спецификации эти реализации включали MRI, YARV, Rubinius, JRuby, IronRuby, MagLev, MacRuby, XRuby, Ruby.NET, Cardinal, tinyrb, RubyGoLightly, SmallRuby, BlueRuby и другие. Особый интерес представляют MRI (который реализует только 1.8) и YARV (который реализует только 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#values имеет тот же порядок, что и Hash#keys, Hash#each_value а также Hash#each_key имеет тот же порядок, что и те, иHash#each_pairа такжеHash#eachиметь такой же порядок.

В тестовом наборе YARV я не нашел ничего, что указывало бы, что порядок сохранен. На самом деле, я ничего не мог найти в этом тестовом комплекте, как раз наоборот: тесты идут очень долго, чтобы избежать зависимости от заказа!

Книга Flanagan/ Matz своего рода Сорта неявно указывает Hash упорядочение букв в разделе 9.5.3.6 Hashитераторы. Во-первых, он использует ту же формулировку, что и документы:

В Ruby 1.9, однако, хеш-элементы повторяются в порядке их вставки, […]

Но тогда это продолжается:

[…], И это порядок, показанный в следующих примерах:

И в этих примерах он насамом деле использует литерал:

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

В своем комментарии mu is too short упомянул, что

h = { a: 1, b: 2 }такой же какh = { }; h[:a] = 1; h[:b] = 2

и в другом комментарии, который

ничто другое не имело бы никакого смысла

К сожалению, это не так

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

Таким образом, в зависимости от того, как вы интерпретируете эту строку из книги, и в зависимости от того, насколько "спецификационно" вы оцениваете эту книгу, да, порядок литералов гарантирован.

Из документации:

Хэши перечисляют свои значения в том порядке, в котором были вставлены соответствующие ключи.

Другие вопросы по тегам