template ruby loops hash order literals

template - ¿Está garantizado el orden de un hash de Ruby?



hash ruby (2)

Ruby, desde v1.9, soporta un orden determinista cuando realiza un hash a través de un hash; Las entradas agregadas primero serán devueltas primero.

¿Se aplica esto a los literales, es decir, { a: 1, b: 2 } siempre dará un antes de b?

Hice un experimento rápido con Ruby 2.1 (MRI) y, de hecho, fue coherente, pero ¿hasta qué punto está garantizado por el lenguaje que funciona en todas las implementaciones de Ruby?


De la documentation :

Los hashes enumeran sus valores en el orden en que se insertaron las claves correspondientes.


Hay un par de lugares donde se podría especificar, es decir, un par de cosas que se consideran "La especificación del lenguaje Ruby":

La especificación ISO no dice nada acerca de la ordenación de Hash : fue escrita de tal manera que todas las implementaciones existentes de Ruby son automáticamente compatibles con ella, sin tener que cambiar, es decir, fue escrita para ser descriptiva de las implementaciones actuales de Ruby, no prescriptiva . En el momento en que se escribió la especificación, esas implementaciones incluían MRI, YARV, Rubinius, JRuby, IronRuby, MagLev, MacRuby, XRuby, Ruby.NET, Cardinal, tinyrb, RubyGoLightly, SmallRuby, BlueRuby y otros. De particular interés son MRI (que solo implementa 1.8) y YARV (que solo implementa 1.9 (en ese momento)), lo que significa que la especificación solo puede especificar el comportamiento que es común a 1.8 y 1.9, que no es el orden de Hash .

El proyecto RubySpec fue abandonado por sus desarrolladores debido a la frustración de que los desarrolladores de Ruby-Core y YARV nunca lo reconocieron. Sin embargo, sí (implícitamente) especifica que los literales de Hash están ordenados de izquierda a derecha :

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

Esa es la especificación para las Hash#keys , sin embargo, las otras especificaciones prueban que los Hash#values tienen el mismo orden que las Hash#keys Hash#each_value , Hash#each_value y Hash#each_key tienen el mismo orden que esas, y Hash#each_pair y Hash#each tienen Hash#each el mismo orden también.

No pude encontrar nada en el testuite de YARV que especifique que se conserva el orden. De hecho, no pude encontrar nada sobre el pedido en ese sitio de prueba, sino todo lo contrario: ¡las pruebas se extienden mucho para evitar depender del pedido!

El libro Flanagan / matz kinda-sorta especifica implícitamente el ordenamiento literal de Hash en la sección 9.5.3.6 iteradores de Hash . Primero, usa mucho la misma formulación que los documentos:

En Ruby 1.9, sin embargo, los elementos hash se iteran en su orden de inserción, […]

Pero luego continúa:

[…], Y ese es el orden que se muestra en los siguientes ejemplos:

Y en esos ejemplos, en realidad usa un 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

En su comentario , @mu es demasiado breve al mencionar que

h = { a: 1, b: 2 } es lo mismo que h = { }; h[:a] = 1; h[:b] = 2 h = { }; h[:a] = 1; h[:b] = 2

y en otro comentario que

nada mas tendria sentido

Desafortunadamente, eso no es verdad:

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

Entonces, dependiendo de cómo interpretas esa línea del libro y de cómo la "especificación-ish" juzgas ese libro, sí, el orden de los literales está garantizado.