Гарантирован ли заказ хеш-букв Ruby?

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

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

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

Ответы

Ответ 1

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

Спецификация ISO ничего не говорит о порядке Hash: она была написана таким образом, что все существующие реализации Ruby автоматически согласуются с ней, без необходимости изменения, т.е. Было написано, чтобы описать текущие реализации Ruby, а не предписывать. В то время, когда была написана спецификация, такие реализации включали МРТ, 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 был оставлен разработчиками из-за разочарования, что разработчики рубинового ядра и разработчики 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 testsuite, который указывает, что упорядочение сохраняется. На самом деле, я ничего не мог найти о заказе в этом testuite, совсем наоборот: тесты идут очень долго, чтобы избежать зависания от заказа!

Фланаган/Мац книга любопытное Сорта неявно указывает 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 слишком коротко упоминается, что

h = { a: 1, b: 2 } совпадает с h = { }; h[:a] = 1; h[: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

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

Ответ 2

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

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