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 de lenguaje Ruby ISO
- el proyecto RubySpec
- el testuite YARV
- El libro de lenguaje de programación Ruby por matz y david flanagan
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 queh = { }; 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.