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?
Tengo algunas observaciones sobre Struct vs. OpenStruct vs. Hash en mi reciente comentario en el blog "Structs inside out" , en caso de que alguien esté interesado.
Robert Klemme
La información sobre la velocidad de Hash, Struct y OpenStruct no está actualizada. Consulte stackoverflow.com/a/43987844/128421 para obtener un punto de referencia más reciente.
The Tin Man
Respuestas:
173
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 Structo 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.
"pero no tienen los métodos de instancia como lo haría una clase". bueno, hay un patrón bastante común para usarlo como una "clase normal":class Point < Struct.new(:x, :y); methods here; end
2011
10
@tokland en cuanto a hoy, el enfoque "preferido" de personalizar la estructura con métodos es pasar el bloque al constructor 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.
Ivan Kolmychek
1
@IvanKolmychek: Genial, en realidad prefiero el enfoque de bloque.
tokland
@tokland bien. Solo quería aclarar que ahora hay un enfoque más agradable, ya que su comentario es altamente votado, por lo que las personas nuevas en Ruby pueden pensar "OK, así es como debe hacerse, porque todos están de acuerdo con eso, correcto ? " :)
Ivan Kolmychek
44
Una pregunta: una vez que llega en el momento en que desea agregar métodos a su estructura, ¿por qué no usar una clase?
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.4300000.2500004.680000(4.683851)OpenStruct fast 4.3800000.2700004.650000(4.649809)Struct slow 0.0900000.0000000.090000(0.094136)Struct fast 0.0800000.0000000.080000(0.078940)
Información muy útil para adictos al rendimiento como yo. Gracias.
Bernardo Oliveira
Me refiero a la implementación de Matz de Ruby (MRI)
Tilo
1
Hola @Tilo, ¿podrías compartir tu código para obtener los resultados anteriores? Quiero usarlo para comparar Struct & OStruct con Hashie :: Mash. Gracias.
Donny Kurnia
1
Hola @Donny, acabo de ver el voto a favor y me di cuenta de que esto se midió en 2011: necesito volver a ejecutar esto con Ruby 2.1: P no estoy seguro si tengo ese código, pero debería ser fácil de reproducir. Intentaré arreglarlo pronto.
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 structdeclaración en C. En Ruby Struct.newtoma un conjunto de nombres de campo como argumentos y devuelve una nueva Clase. De manera similar, en C, una structdeclaració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
Esta es una gran respuesta de la diferencia conceptual entre ellos. Gracias por señalar el anonimato de OpenStruct, creo que eso lo deja mucho más claro.
Bryant
¡Gran explicación!
Yuri Ghensev
24
OpenStructs usa significativamente más memoria y tienen un rendimiento más lento en comparación con Structs.
Pero sabe que la prueba OpenStruct crea muchos hashes temporales. Sugiero un punto de referencia ligeramente modificado, que aún respalda su veredicto (ver más abajo).
Gracias por el ejemplo Ayuda mucho entender en la práctica.
Ahsan
5
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.6000000.0000003.600000(3.755142)Hashie::Mash fast 3.0000000.0000003.000000(3.318067)OpenStruct slow 11.2000000.01000011.210000(12.095004)OpenStruct fast 10.9000000.00000010.900000(12.669553)Struct slow 0.3700000.0000000.370000(0.470550)Struct fast 0.1400000.0000000.140000(0.145161)
Su punto de referencia es realmente extraño. Obtuve el siguiente resultado con ruby2.1.1 en un i5 mac: gist.github.com/nicolas-besnard/…
cappie013
Bueno, el resultado variará entre la versión de ruby utilizada y el hardware utilizado para ejecutarlo. Pero el patrón sigue siendo el mismo, OpenStruct es el más lento, Struct es el más rápido. Hashie cae en el medio.
Donny Kurnia
0
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 OpenStructoperació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 OpenStructse 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
Respuestas:
Con un
OpenStruct
, puede crear atributos arbitrariamente. AStruct
, 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 unStruct
oOpenStruct
. 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.fuente
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:
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)
fuente
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]:
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 .
fuente
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 RubyStruct.new
toma un conjunto de nombres de campo como argumentos y devuelve una nueva Clase. De manera similar, en C, unastruct
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í:
C:
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í:
C:
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.
Las estructuras pueden ser útiles para las definiciones de clase abreviadas.
fuente
OpenStructs usa significativamente más memoria y tienen un rendimiento más lento en comparación con Structs.
En mi sistema, el siguiente código se ejecutó en 14 segundos y consumió 1,5 GB de memoria. Su kilometraje puede variar:
Eso terminó casi instantáneamente y consumió 26.6 MB de memoria.
fuente
Struct
:OpenStruct
:fuente
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?
fuente
Usando el código @Robert, agregué Hashie :: Mash al elemento de referencia y obtuve este resultado:
fuente
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 noOpenStruct
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 # openstructsfuente