web-dev-qa-db-fra.com

L'ordre d'un Ruby littéral de hachage est-il garanti?

Ruby, depuis la version 1.9, prend en charge un ordre déterministe lors de la boucle à travers un hachage; les entrées ajoutées en premier seront retournées en premier.

Cela s'applique-t-il aux littéraux, c'est-à-dire { a: 1, b: 2 } donne toujours a avant b?

J'ai fait une rapide expérience avec Ruby 2.1 (MRI) et c'était en fait cohérent, mais dans quelle mesure le langage garantit-il que tout fonctionne Ruby = implémentations?

30
mahemoff

Il y a quelques endroits où cela pourrait être spécifié, c'est-à-dire quelques éléments qui sont considérés comme "Le Ruby Language Spécification":

La spécification ISO ne dit rien sur l'ordre des Hash: elle a été écrite de telle manière que toutes les implémentations existantes de Ruby sont automatiquement conformes avec elle, sans avoir à changer, ie il a été écrit pour être descriptif des implémentations actuelles Ruby, et non prescriptives . Au moment de la rédaction de la spécification, ces implémentations incluaient l'IRM, YARV, Rubinius, JRuby, IronRuby, MagLev, MacRuby, XRuby, Ruby.NET, Cardinal, tinyrb, RubyGoLightly, SmallRuby, BlueRuby, L'IRM (qui uniquement implémente 1.8) et YARV (qui uniquement implémente 1.9 (à l'époque)), ce qui signifie que la spécification ne peut spécifier qu'un comportement commun à 1.8 et 1.9, ce que l'ordre de Hash n'est pas.

Le projet RubySpec a été abandonné par ses développeurs par frustration que les développeurs Ruby-core et les développeurs YARV ne l'aient jamais reconnu. Il le fait cependant (implicitement) spécifiez que les littéraux Hash sont ordonnés de gauche à droite :

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

C'est la spécification pour Hash#keys, cependant, les autres spécifications testent que Hash#values a le même ordre que Hash#keys, Hash#each_value et Hash#each_key a le même ordre que ceux-ci, et Hash#each_pair et Hash#each ont également le même ordre.

Je n'ai rien trouvé dans la suite de tests YARV qui spécifie que l'ordre est préservé. En fait, je n'ai rien trouvé du tout sur la commande dans cette suite de tests, bien au contraire: les tests vont très loin jusqu'à éviter selon la commande!

Le livre Flanagan/matz sorte-sorta spécifie implicitement Hash l'ordre littéral dans la section 9.5.3.6 Hash itérateurs . Tout d'abord, il utilise à peu près la même formulation que les documents:

Dans Ruby 1.9, cependant, les éléments de hachage sont itérés dans leur ordre d'insertion, […]

Mais ça continue:

[…], Et c'est l'ordre indiqué dans les exemples suivants:

Et dans ces exemples, il utilise en fait un littéral:

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

Dans son commentaire , @ mu est trop court a mentionné que

h = { a: 1, b: 2 } est le même que h = { }; h[:a] = 1; h[:b] = 2

et dans n autre commentaire que

rien d'autre n'aurait de sens

Malheureusement, ce n'est pas vrai:

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

Donc, selon la façon dont vous interprétez cette ligne du livre et selon la façon dont "spécification-ish" vous jugez ce livre, oui, l'ordre des littéraux est garanti.

29
Jörg W Mittag

De la documentation :

Les hachages énumèrent leurs valeurs dans l'ordre dans lequel les clés correspondantes ont été insérées.

9
Exupery