2010-07-18 12 views
57

No tengo mucha experiencia en programación. Pero, para mí, Struct parece algo similar a Hash.Cuándo usar Struct en lugar de Hash en Ruby?

  • ¿Qué puede hacer Struct así?
  • ¿Hay algo que Struct pueda hacer, que Hash no puede hacer?

Tras Google, el concepto de Struct es importante en C, pero no sé mucho acerca de C.

Respuesta

75

Las estructuras difieren de usar HashMaps de las siguientes maneras (además de cómo se ve el código):

  • una estructura cuenta con un conjunto fijo de atributos, mientras añade nuevas claves a un hash.
  • Llamar a un atributo que no existe en una instancia de una estructura provocará un NoMethodError, mientras que obtener el valor para una clave no existente de un hash simplemente devolverá nil.
  • Dos instancias de diferentes estructuras nunca serán iguales, incluso si las estructuras tienen los mismos atributos y las instancias tienen los mismos valores (es decir, Struct.new(:x).new(42) == Struct.new(:x).new(42) es falso, mientras que Foo = Struct.new(:x); Foo.new(42)==Foo.new(42) es verdadero).
  • El método to_a de estructuras devuelve una matriz de valores, mientras que to_a en un hash que obtiene una matriz de clave y valor de pares (donde "par" significa "array de dos elementos")
  • Si Foo = Struct.new(:x, :y, :z) que puede hacer Foo.new(1,2,3) para crear una instancia de Foo sin tener que deletrear los nombres de los atributos.

Para responder a la pregunta: Cuando desee modelar objetos con un conjunto conocido de atributos, use structs. Cuando desee modelar el uso arbitrario de los hashmaps (por ejemplo, contar la frecuencia con que cada palabra aparece en una cadena o asignar apodos a los nombres completos, etc. definitivamente no son trabajos para una estructura, mientras que modelar a una persona con un nombre, una edad y una dirección sería un ajuste perfecto para Person = Struct.new(name, age, address)).

Como nota al margen: las estructuras de C tienen poco o nada que ver con estructuras de rubí, por lo que no se deje conseguir confundido por eso.

+0

Sus otros puntos son los correctos (por lo que 1 para eso), pero [ 'Struct # == '] (http://ruby-doc.org/core/classes/Struct.html#M000890) funciona de manera diferente a lo que explicaste cuando realmente almacena el resultado de' Struct.new' en lugar de llamarlo dos veces con los mismos argumentos –

+0

@MarkRushakoff: Si hago 'Foo = Struct.new (: x); Bar = Struct.new (: x) 'y luego hacer' Foo.new (42) == Bar.new (42) 'Voy a obtener falso. Si hago 'Foo.new (42) == Foo.new (42)' Me haré realidad. Y si lees con cuidado, eso es exactamente lo que dije (Dos instancias de * diferentes * estructuras "). – sepp2k

+0

Veo lo que quieres decir. No estaba claro para mí porque no lo contrastabas con una explicación de que la igualdad funciona como se espera cuando se utiliza el mismo tipo de estructura –

9

Desde el Struct documentación:

una estructura es una forma conveniente de agrupe una cantidad de atributos, utilizando métodos de acceso, sin tener que escribir una clase explícita.

Por otro lado, un Hash:

A Hash es una colección de pares de clave y valor. Es similar a una matriz, excepto que la indexación se realiza mediante claves arbitrarias de cualquier tipo de objeto, no como un índice entero. El orden en el que recorre un hash por clave o valor puede parecer arbitrario, y generalmente no estará en el orden de inserción.

La principal diferencia es cómo accede a sus datos.

ruby-1.9.1-p378 > Point = Struct.new(:x, :y) 
=> Point 
ruby-1.9.1-p378 > p = Point.new(4,5) 
=> #<struct Point x=4, y=5> 
ruby-1.9.1-p378 > p.x 
=> 4 
ruby-1.9.1-p378 > p.y 
=> 5 
ruby-1.9.1-p378 > p = {:x => 4, :y => 5} 
=> {:x=>4, :y=>5} 
ruby-1.9.1-p378 > p.x 
NoMethodError: undefined method `x' for {:x=>4, :y=>5}:Hash 
    from (irb):7 
    from /Users/mr/.rvm/rubies/ruby-1.9.1-p378/bin/irb:17:in `<main>' 
ruby-1.9.1-p378 > p[:x] 
=> 4 
ruby-1.9.1-p378 > p[:y] 
=> 5 

En resumen, se haría un nuevo Struct cuando se desea una clase que es un "plain old data" structure (opcionalmente con la intención de ampliarlo con más métodos), y se utilizaría un hash cuando no se necesita una tipo formal en absoluto.

33

Sé que esta pregunta fue casi bien respondió-, pero, sorprendentemente, nadie ha hablado de una de las diferencias más grandes y los beneficios reales de Struct. Y supongo que es por eso que somebody is still asking.

Entiendo las diferencias, pero ¿cuál es la verdadera ventaja de utilizar un Struct sobre un Hash, cuando un Hash puede hacer lo mismo y es más fácil de tratar? Parece que las estructuras son superfluas.

Struct es más rápido.

require 'benchmark' 

Benchmark.bm 10 do |bench| 
    bench.report "Hash: " do 
    50_000_000.times do { name: "John Smith", age: 45 } end 
    end 

    bench.report "Struct: " do 
    klass = Struct.new(:name, :age) 
    50_000_000.times do klass.new("John Smith", 45) end 
    end 

end 

# ruby 2.2.2p95 (2015-04-13 revision 50295) [x64-mingw32]. 
#     user  system  total  real 
# Hash:  22.340000 0.016000 22.356000 (24.260674) 
# Struct:  12.979000 0.000000 12.979000 (14.095455) 

# ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin11.0] 
# 
#     user  system  total  real 
# Hash:  31.980000 0.060000 32.040000 (32.039914) 
# Struct:  16.880000 0.010000 16.890000 (16.886061) 
+3

Quizás esto sea de interés: vuelvo a ejecutar su punto de referencia una vez con Cygwin Ruby (2.2.3p173) y obtuve para el usuario tiempos de 62.462 para el Hash y 19.875. Luego lo vuelvo a ejecutar con JRuby 1.7.23 en JVM 1.7 y obtuve 8.401 para Hash y 8.701 para Struct. Mientras que la ventaja de velocidad es grande en Ruby, ambos parecen tener la misma velocidad bajo JRuby. – user1934428

+0

Me pregunto qué consume menos memoria. – Nakilon

8

Una diferencia más es que puede agregar métodos de comportamiento a un Struct.

Customer = Struct.new(:name, :address) do 

    def greeting; "Hello #{name}!" ; end 

end 

Customer.new("Dave", "123 Main").greeting # => "Hello Dave!" 
+0

Creo que esta es una gran diferencia que justificaría Struct vs Hash. Dado que según la convención de Rails no debería haber dos clases en el mismo archivo ruby ​​(debido a problemas de carga automática), muchas veces Struct es una gran manera de crear un reemplazo de clase que actúa como presentador/decorador. – sandre89

0

Si sólo vamos a encapsular los datos, entonces un hash (o un conjunto de valores hash) están bien. Si usted está planeando tener manipular los datos o interactuar con otros datos, a continuación, un Struct puede abrir algunas posibilidades interesantes:

Point = Struct.new(:x, :y) 
point_a = Point.new(0,0) 
point_b = Point.new(2,3) 

class Point 
    def distance_to another_point 
    Math.sqrt((self.x - another_point.x)**2 + (self.y - another_point.y)**2) 
    end 
end 

puts point_a.distance_to point_b 
+0

Puedes hacer 'clase Punto << Hash' y todo será igual. – Nakilon

Cuestiones relacionadas