rails - ruby struct example
¿Cuándo debería usar Struct vs. OpenStruct? (8)
Eche un vistazo a la API con respecto al nuevo método. Muchas de las diferencias se pueden encontrar allí.
Personalmente, me gusta bastante OpenStruct, ya que no tengo que definir la estructura del objeto de antemano, y simplemente agregar cosas como quiero. Supongo que esa sería su principal (des) ventaja?
En general, ¿cuáles son las ventajas y desventajas de usar un OpenStruct en comparación con un Struct? ¿Qué tipo de casos de uso generales encajarían en cada uno de estos?
Las OpenStructs usan mucha más memoria y son más lentas en comparación con Structs.
require ''ostruct''
collection = (1..100000).collect do |index|
OpenStruct.new(:name => "User", :age => 21)
end
En mi sistema, el siguiente código se ejecutó en 14 segundos y consumió 1,5 GB de memoria. Su millaje puede variar:
User = Struct.new(:name, :age)
collection = (1..100000).collect do |index|
User.new("User",21)
end
Eso terminó casi instantáneamente y consumió 26.6 MB de memoria.
Los casos de uso para los dos son bastante diferentes.
Puedes pensar en la clase Struct en Ruby 1.9 como equivalente a la declaración struct
en C. En Ruby Struct.new
toma un conjunto de nombres de campo como argumentos y devuelve una nueva clase. De forma similar, en C, una declaración de struct
toma un conjunto de campos y le permite al programador usar el nuevo tipo complejo tal como lo haría con cualquier tipo incorporado.
Rubí:
Newtype = Struct.new(:data1, :data2)
n = Newtype.new
DO:
typedef struct {
int data1;
char data2;
} newtype;
newtype n;
La clase OpenStruct se puede comparar con una declaración de estructura anónima en C. Permite al programador crear una instancia de un tipo complejo.
Rubí:
o = OpenStruct.new(data1: 0, data2: 0)
o.data1 = 1
o.data2 = 2
DO:
struct {
int data1;
char data2;
} o;
o.data1 = 1;
o.data2 = 2;
Aquí hay algunos casos de uso común.
OpenStructs se puede usar para convertir fácilmente hashes en objetos únicos que responden a todas las claves hash.
h = { a: 1, b: 2 }
o = OpenStruct.new(h)
o.a = 1
o.b = 2
Las estructuras pueden ser útiles para definiciones de clase taquigráficas.
class MyClass < Struct.new(:a,:b,:c)
end
m = MyClass.new
m.a = 1
Otro punto de referencia:
require ''benchmark''
require ''ostruct''
REP = 100000
User = Struct.new(:name, :age)
USER = "User".freeze
AGE = 21
HASH = {:name => USER, :age => AGE}.freeze
Benchmark.bm 20 do |x|
x.report ''OpenStruct slow'' do
REP.times do |index|
OpenStruct.new(:name => "User", :age => 21)
end
end
x.report ''OpenStruct fast'' do
REP.times do |index|
OpenStruct.new(HASH)
end
end
x.report ''Struct slow'' do
REP.times do |index|
User.new("User", 21)
end
end
x.report ''Struct fast'' do
REP.times do |index|
User.new(USER, AGE)
end
end
end
Para los impacientes que quieran hacerse una idea de los resultados de referencia, sin ejecutarlos ellos mismos, aquí está el resultado del código anterior (en un MB Pro 2.4GHz i7)
user system total real
OpenStruct slow 4.430000 0.250000 4.680000 ( 4.683851)
OpenStruct fast 4.380000 0.270000 4.650000 ( 4.649809)
Struct slow 0.090000 0.000000 0.090000 ( 0.094136)
Struct fast 0.080000 0.000000 0.080000 ( 0.078940)
Utilizando el código @Robert, agrego Hashie :: Mash al elemento de referencia y obtuve este resultado:
user system total real
Hashie::Mash slow 3.600000 0.000000 3.600000 ( 3.755142)
Hashie::Mash fast 3.000000 0.000000 3.000000 ( 3.318067)
OpenStruct slow 11.200000 0.010000 11.210000 ( 12.095004)
OpenStruct fast 10.900000 0.000000 10.900000 ( 12.669553)
Struct slow 0.370000 0.000000 0.370000 ( 0.470550)
Struct fast 0.140000 0.000000 0.140000 ( 0.145161)
Struct
:
>> s = Struct.new(:a, :b).new(1, 2)
=> #<struct a=1, b=2>
>> s.a
=> 1
>> s.b
=> 2
>> s.c
NoMethodError: undefined method `c` for #<struct a=1, b=2>
OpenStruct
:
>> require ''ostruct''
=> true
>> os = OpenStruct.new(a: 1, b: 2)
=> #<OpenStruct a=1, b=2>
>> os.a
=> 1
>> os.b
=> 2
>> os.c
=> nil
ACTUALIZAR:
A partir de Ruby 2.4.1, OpenStruct y Struct están mucho más cerca en velocidad. Ver https://.com/a/43987844/128421
PREVIAMENTE:
Para completar: Struct vs. Class vs. Hash vs. OpenStruct
Ejecutando código similar al de burtlo, en Ruby 1.9.2, (1 de 4 núcleos x86_64, 8GB RAM) [tabla editada para alinear columnas]:
creating 1 Mio Structs : 1.43 sec , 219 MB / 90MB (virt/res) creating 1 Mio Class instances : 1.43 sec , 219 MB / 90MB (virt/res) creating 1 Mio Hashes : 4.46 sec , 493 MB / 364MB (virt/res) creating 1 Mio OpenStructs : 415.13 sec , 2464 MB / 2.3GB (virt/res) # ~100x slower than Hashes creating 100K OpenStructs : 10.96 sec , 369 MB / 242MB (virt/res)
Las OpenStructs son poco frecuentes y requieren mucha memoria , y no se adaptan bien a grandes conjuntos de datos
Crear 1 Mio OpenStructs es ~ 100x más lento que crear 1 Mio Hashes .
start = Time.now
collection = (1..10**6).collect do |i|
{:name => "User" , :age => 21}
end; 1
stop = Time.now
puts "#{stop - start} seconds elapsed"
Con OpenStruct
, puede crear atributos arbitrariamente. Un Struct
, por otro lado, debe tener sus atributos definidos cuando lo crea. La elección de uno sobre el otro debe basarse principalmente en si necesita poder agregar atributos más tarde.
La manera de pensar sobre ellos es como el término medio del espectro entre Hashes por un lado y las clases por el otro. Implican una relación más concreta entre los datos que un Hash
, pero no tienen los métodos de instancia como una clase. Un montón de opciones para una función, por ejemplo, tiene sentido en un hash; solo están vagamente relacionados. El nombre, el correo electrónico y el número de teléfono que necesita una función se pueden empaquetar juntos en un Struct
o OpenStruct
. Si ese nombre, correo electrónico y número de teléfono necesitan métodos para proporcionar el nombre en los formatos "Primero el último" y "Último, primero", entonces debe crear una clase para manejarlo.