Гарантирован ли порядок литерала Ruby хеша?
Ruby, начиная с v1.9, поддерживает детерминированный порядок при циклическом прохождении хеша; Записи, добавленные первыми, будут возвращены первыми.
Относится ли это к литералам, т.е. { a: 1, b: 2 }
всегда приносить до б?
Я провел быстрый эксперимент с Ruby 2.1 (MRI), и он действительно был непротиворечивым, но в какой степени это гарантировано языком для работы на всех реализациях Ruby?
2 ответа
Есть несколько мест, где это может быть указано, т.е. пара вещей, которые считаются "Спецификацией языка Ruby":
- спецификация языка Ruby ISO
- проект RubySpec
- тестовый набор YARV
- Книга о языке программирования 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
Таким образом, в зависимости от того, как вы интерпретируете эту строку из книги, и в зависимости от того, насколько "спецификационно" вы оцениваете эту книгу, да, порядок литералов гарантирован.
Из документации:
Хэши перечисляют свои значения в том порядке, в котором были вставлены соответствующие ключи.