2009-05-01 16 views
44

un rubí Struct permite una instancia que se genere con un conjunto de descriptores de acceso:¿Cuándo es mejor usar un Struct en lugar de un Hash en Ruby?

# Create a structure named by its constant 
Customer = Struct.new(:name, :address)  #=> Customer 
Customer.new("Dave", "123 Main")   #=> #<Customer name="Dave", address="123 Main"> 

Esto se ve cómodo y eficaz, sin embargo, un hash hace algo muy similar:

Customer = {:name => "Dave", :address => "123 Main"} 

¿Cuáles son los reales - situaciones del mundo en las que preferiría una Struct (y por qué), y ¿cuáles son las salvedades o dificultades al elegir una sobre la otra?

+0

Consideraría una estructura más fácil de entender, es decir, que conduce a un código más fácil de mantener. Dejaré que otra persona haga comentarios sobre las ventajas de rendimiento. –

+1

También vale la pena señalar que las estructuras superan a Hashes en términos de velocidad de recuperación de datos, lo que las convierte en mejores opciones para cualquier configuración a la que se deba acceder repetidamente durante el tiempo de ejecución. – user2398029

+2

Consulte la sección "Estructura vs. OpenStruct vs. Hash" en [Estructura de adentro hacia afuera] (http://blog.rubybestpractices.com/posts/rklemme/017-Struct.html). –

Respuesta

20

Personalmente, utilizo una estructura en los casos en que quiero hacer que una parte de los datos actúe como una colección de datos en lugar de acoplar libremente bajo un Hash.

Por ejemplo, yo he hecho un script que descarga videos de Youtube y de ahí que he una estructura para representar un video y para comprobar si todos los datos son en su lugar:


Video = Struct.new(:title, :video_id, :id) do 
    def to_s 
    "http://youtube.com/get_video.php?t=#{id}&video_id=#{video_id}&fmt=18" 
    end 

    def empty? 
    @title.nil? and @video_id.nil? and @id.nil? 
    end 
end 

Más adelante en mi código Tengo un bucle que recorre todas las filas de la página HTML fuente de los videos hasta que empty? no devuelve verdadero.

Otro ejemplo que he visto es James Edward Gray IIsconfiguration class que utiliza OpenStruct para agregar fácilmente las variables de configuración cargadas desde un archivo externo:

#!/usr/bin/env ruby -wKU 

require "ostruct" 

module Config 
    module_function 

    def load_config_file(path) 
    eval <<-END_CONFIG 
    config = OpenStruct.new 
    #{File.read(path)} 
    config 
    END_CONFIG 
    end 
end 

# configuration_file.rb 
config.db = File.join(ENV['HOME'], '.cool-program.db') 
config.user = ENV['USER'] 

# Usage: 
Config = Config.load_config('configuration_file.rb') 
Config.db # => /home/ba/.cool-program.db 
Config.user # => ba 
Config.non_existant # => Nil 

La diferencia entre Struct y OpenStruct es que Struct sólo responde a los atributos que se 'set set, OpenStruct responde a cualquier conjunto de atributos, pero aquellos sin ningún valor establecido devolverán Nil

+0

¿Por qué no se muestra todo el código que escribí aquí pero si voy a editar la publicación lo veo todo y se ve genial en la vista previa? – gaqzi

+0

El << lo jodió, lo convirtió en < < y ahora todo es genial. :) – gaqzi

+1

Gracias, estos son buenos ejemplos. El primero parece demostrar claramente las ventajas de Struct. El segundo, aunque es un buen ejemplo, funcionaría tan bien como Hash, ¿no es así? –

10

A Struct tiene la característica que puede obtener en su e elementos por índice así como por nombre:

irb(main):004:0> Person = Struct.new(:name, :age) 
=> Person 
irb(main):005:0> p = Person.new("fred", 26) 
=> # 
irb(main):006:0> p[0] 
=> "fred" 
irb(main):007:0> p[1] 
=> 26 
irb(main):008:0> p.name 
=> "fred" 
irb(main):009:0> p.age 
=> 26

que a veces es útil.

6

Es principalmente el rendimiento. Struct es mucho más rápido, por orden de magnitud. Y consume menos memoria cuando se compara con Hash o OpenStruct. Más información aquí: When should I use Struct vs. OpenStruct?

+0

Esta respuesta debe ser actualizada y su enlace es la respuesta mucho más profunda aquí. El rendimiento es un orden de magnitud o más mejor. – whistler

+0

Struct es más rápido? Tenga cuidado con la información anterior. –

+1

Perdón, ¿qué? Convertir el coma en Hash es substiantly más rápido por orden de magnitud en Ruby 2.0 y aún 10 veces más rápido en Ruby 2.4 en mi computadora portátil. – lzap

3

Con respecto a los comentarios sobre la velocidad de uso de Hashes, Struct o OpenStruct: Hash siempre ganará para uso general. Es la base de OpenStruct sin la formación adicional de hielo, por lo que no es tan flexible, pero es delgado y malo.

Uso de Ruby 2.4.1:

require 'fruity' 
require 'ostruct' 

def _hash 
    h = {} 
    h['a'] = 1 
    h['a'] 
end 

def _struct 
    s = Struct.new(:a) 
    foo = s.new(1) 
    foo.a 
end 

def _ostruct 
    person = OpenStruct.new 
    person.a = 1 
    person.a 
end 

compare do 
    a_hash { _hash } 
    a_struct { _struct } 
    an_ostruct { _ostruct } 
end 

# >> Running each test 4096 times. Test will take about 2 seconds. 
# >> a_hash is faster than an_ostruct by 13x ± 1.0 
# >> an_ostruct is similar to a_struct 

Usando definiciones más concisa de los hash y OpenStruct:

require 'fruity' 
require 'ostruct' 

def _hash 
    h = {'a' => 1} 
    h['a'] 
end 

def _struct 
    s = Struct.new(:a) 
    foo = s.new(1) 
    foo.a 
end 

def _ostruct 
    person = OpenStruct.new('a' => 1) 
    person.a 
end 

compare do 
    a_hash { _hash } 
    a_struct { _struct } 
    an_ostruct { _ostruct } 
end 

# >> Running each test 4096 times. Test will take about 2 seconds. 
# >> a_hash is faster than an_ostruct by 17x ± 10.0 
# >> an_ostruct is similar to a_struct 

Si la estructura, Hash o Struct o OpenStruct se define una vez y luego utilizado muchas veces , entonces la velocidad de acceso se vuelve más importante, y una Struct comienza a brillar:

require 'fruity' 
require 'ostruct' 

HSH = {'a' => 1} 
def _hash 
    HSH['a'] 
end 

STRCT = Struct.new(:a).new(1) 
def _struct 
    STRCT.a 
end 

OSTRCT = OpenStruct.new('a' => 1) 
def _ostruct 
    OSTRCT.a 
end 

puts "Ruby version: #{RUBY_VERSION}" 

compare do 
    a_hash { _hash } 
    a_struct { _struct } 
    an_ostruct { _ostruct } 
end 

# >> Ruby version: 2.4.1 
# >> Running each test 65536 times. Test will take about 2 seconds. 
# >> a_struct is faster than a_hash by 4x ± 1.0 
# >> a_hash is similar to an_ostruct 

Sin embargo, tenga en cuenta que el Struct es solo 4 veces más rápido que el Hash para acceder, mientras que el Hash es 17 veces más rápido para la inicialización, la asignación y el acceso. Tendrá que descubrir cuál es el mejor para usar en función de las necesidades de una aplicación en particular.Tiendo a usar Hashes para uso general como resultado.

Además, la velocidad de uso de OpenStruct ha mejorado enormemente a lo largo de los años; Lo que solía ser más lento que Struct basado en puntos de referencia que he visto en el pasado y que comparan a 1.9.3-P551:

require 'fruity' 
require 'ostruct' 

def _hash 
    h = {} 
    h['a'] = 1 
    h['a'] 
end 

def _struct 
    s = Struct.new(:a) 
    foo = s.new(1) 
    foo.a 
end 

def _ostruct 
    person = OpenStruct.new 
    person.a = 1 
    person.a 
end 

puts "Ruby version: #{RUBY_VERSION}" 

compare do 
    a_hash { _hash } 
    a_struct { _struct } 
    an_ostruct { _ostruct } 
end 

# >> Ruby version: 1.9.3 
# >> Running each test 4096 times. Test will take about 2 seconds. 
# >> a_hash is faster than a_struct by 7x ± 1.0 
# >> a_struct is faster than an_ostruct by 2x ± 0.1 

y:

require 'fruity' 
require 'ostruct' 

def _hash 
    h = {'a' => 1} 
    h['a'] 
end 

def _struct 
    s = Struct.new(:a) 
    foo = s.new(1) 
    foo.a 
end 

def _ostruct 
    person = OpenStruct.new('a' => 1) 
    person.a 
end 

puts "Ruby version: #{RUBY_VERSION}" 

compare do 
    a_hash { _hash } 
    a_struct { _struct } 
    an_ostruct { _ostruct } 
end 

# >> Ruby version: 1.9.3 
# >> Running each test 4096 times. Test will take about 2 seconds. 
# >> a_hash is faster than a_struct by 7x ± 1.0 
# >> a_struct is faster than an_ostruct by 2x ± 1.0 

y:

require 'fruity' 
require 'ostruct' 

HSH = {'a' => 1} 
def _hash 
    HSH['a'] 
end 

STRCT = Struct.new(:a).new(1) 
def _struct 
    STRCT.a 
end 

OSTRCT = OpenStruct.new('a' => 1) 
def _ostruct 
    OSTRCT.a 
end 

puts "Ruby version: #{RUBY_VERSION}" 

compare do 
    a_hash { _hash } 
    a_struct { _struct } 
    an_ostruct { _ostruct } 
end 

# >> Ruby version: 1.9.3 
# >> Running each test 32768 times. Test will take about 1 second. 
# >> a_struct is faster than an_ostruct by 3x ± 1.0 
# >> an_ostruct is similar to a_hash 
+0

Este es un caso de borde (un acceso) y usted está combinando combinación de inicialización, obtener y establecer. Lo que generalmente es más importante es la velocidad de acceso. Pero buena observación sobre la mejora en el tiempo. – lzap

+0

Estoy comparando la velocidad general, cuando se incluye la inicialización en la prueba, y la velocidad de acceso solamente, ya que hay diferencias, y diferentes usos pueden enfatizar uno sobre el otro. –

Cuestiones relacionadas