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?
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?
Respuestas:
Con un OpenStruct
, puede crear atributos arbitrariamente. A Struct
, por otro lado, debe tener sus atributos definidos cuando lo crea. La elección de uno sobre el otro debería basarse principalmente en si necesita poder agregar atributos más adelante.
La forma de pensar en 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 la de a Hash
, pero no tienen los métodos de instancia como lo haría 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 agrupar en un Struct
o OpenStruct
. Si ese nombre, correo electrónico y número de teléfono necesitaban métodos para proporcionar el nombre en los formatos "Primero, Último" y "Último, Primero", entonces debe crear una clase para manejarlo.
class Point < Struct.new(:x, :y); methods here; end
Point = Struct.new(:x, :y) { methods here }
. ( fuente ) Por supuesto, { ... }
puede escribirse como un bloque de varias líneas ( do ... end
) y, creo, esa es la forma preferida.
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 quieren tener una idea de los resultados de referencia, sin ejecutarlos ellos mismos, aquí está la salida 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)
ACTUALIZAR:
A partir de Ruby 2.4.1 OpenStruct y Struct están mucho más cerca en velocidad. Ver https://stackoverflow.com/a/43987844/128421
PREVIAMENTE:
Para completar: Struct vs. Class vs. Hash vs. OpenStruct
Ejecutando un código similar al de Burtlo, en Ruby 1.9.2, (1 de 4 núcleos x86_64, 8GB de RAM) [tabla editada para alinear columnas]:
creando 1 Mio Structs: 1.43 sec, 219 MB / 90MB (virt / res) creando 1 instancia de Mio Class: 1.43 segundos, 219 MB / 90MB (virt / res) creando 1 Mio Hashes: 4.46 seg, 493 MB / 364MB (virt / res) creando 1 Mio OpenStructs: 415.13 segundos, 2464 MB / 2.3GB (virt / res) # ~ 100 veces más lento que Hashes creando 100K OpenStructs: 10.96 segundos, 369 MB / 242MB (virt / res)
OpenStructs son sloooooow y mucha memoria , y no se adaptan bien para grandes conjuntos de datos
Crear 1 Mio OpenStructs es ~ 100 veces 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"
Los casos de uso para los dos son bastante diferentes.
Puede pensar en la clase Struct en Ruby 1.9 como un equivalente a la struct
declaración en C. En Ruby Struct.new
toma un conjunto de nombres de campo como argumentos y devuelve una nueva Clase. De manera similar, en C, una struct
declaración toma un conjunto de campos y le permite al programador usar el nuevo tipo complejo como lo haría con cualquier tipo incorporado.
Rubí:
Newtype = Struct.new(:data1, :data2)
n = Newtype.new
C:
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
C:
struct {
int data1;
char data2;
} o;
o.data1 = 1;
o.data2 = 2;
Aquí hay algunos casos de uso comunes.
OpenStructs se puede utilizar 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 las definiciones de clase abreviadas.
class MyClass < Struct.new(:a,:b,:c)
end
m = MyClass.new
m.a = 1
OpenStructs usa significativamente más memoria y tienen un rendimiento más lento 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 kilometraje 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.
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
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 quiera. Supongo que esa sería su principal (des) ventaja?
Usando el código @Robert, agregué 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)
En realidad, no es una respuesta a la pregunta, pero es una consideración muy importante si te importa el rendimiento . Tenga en cuenta que cada vez que crea una OpenStruct
operación, se borra la caché del método, lo que significa que su aplicación funcionará más lentamente. La lentitud o no de no OpenStruct
se trata solo de cómo funciona por sí misma, sino de las implicaciones que su uso trae a toda la aplicación: https://github.com/charliesome/charlie.bz/blob/master/posts/things-that -clear-rubys-method-cache.md # openstructs