Quand devrais-je utiliser Struct contre OpenStruct?
184
En général, quels sont les avantages et les inconvénients de l'utilisation d'un OpenStruct par rapport à un Struct? Quel type de cas d'utilisation généraux conviendrait à chacun de ces cas?
J'ai quelques remarques sur Struct vs OpenStruct vs Hash dans mon récent commentaire de blog "Structs inside out" , juste au cas où quelqu'un serait intéressé.
Robert Klemme
Les informations concernant la vitesse de Hash, Struct et OpenStruct sont obsolètes. Voir stackoverflow.com/a/43987844/128421 pour une référence plus récente.
the Tin Man
Réponses:
173
Avec un OpenStruct, vous pouvez créer arbitrairement des attributs. A Struct, d'autre part, doit avoir ses attributs définis lorsque vous le créez. Le choix de l'un par rapport à l'autre doit être basé principalement sur la question de savoir si vous devez être en mesure d'ajouter des attributs ultérieurement.
La façon de penser à eux est comme le milieu du spectre entre les hachages d'un côté et les classes de l'autre. Ils impliquent une relation plus concrète entre les données que ne le fait a Hash, mais ils n'ont pas les méthodes d'instance comme le ferait une classe. Un tas d'options pour une fonction, par exemple, a du sens dans un hachage; ils ne sont que vaguement liés. Un nom, une adresse e-mail et un numéro de téléphone nécessaires à une fonction peuvent être regroupés dans un fichier Structou OpenStruct. Si ce nom, cette adresse e-mail et ce numéro de téléphone ont besoin de méthodes pour fournir le nom aux formats «First Last» et «Last, First», vous devez créer une classe pour le gérer.
"mais ils n'ont pas les méthodes d'instance comme le ferait une classe". eh bien, il existe un modèle assez courant pour l'utiliser comme "classe normale":class Point < Struct.new(:x, :y); methods here; end
tokland
10
@tokland comme pour aujourd'hui, l'approche "préférée" de la personnalisation de la structure avec des méthodes est de passer le bloc au constructeur Point = Struct.new(:x, :y) { methods here }. ( source ) Bien sûr, { ... }il peut être écrit sous forme de bloc multi-lignes ( do ... end) et, je pense, c'est la méthode préférée.
Ivan Kolmychek
1
@IvanKolmychek: Cool, en fait je préfère l'approche par bloc.
tokland
@tokland bien. Je voulais juste préciser que maintenant il y a une approche plus agréable, étant donné que votre commentaire est fortement voté, donc les personnes qui découvrent ruby peuvent réellement penser "OK, alors c'est comme ça que cela devrait être fait, parce que tout le monde est d'accord avec ça, non ? " :)
Ivan Kolmychek
4
Une question: une fois arrivé au moment où vous souhaitez ajouter des méthodes à votre structure, pourquoi ne pas utiliser une classe?
Pour les impatients qui veulent se faire une idée des résultats du benchmark, sans les exécuter eux-mêmes, voici la sortie du code ci-dessus (sur 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)
Par souci d' exhaustivité: Struct vs Class vs Hash vs OpenStruct
Exécution d'un code similaire à celui de Burtlo, sur Ruby 1.9.2, (1 des 4 cœurs x86_64, 8 Go de RAM) [table modifiée pour aligner les colonnes]:
création de 1 Mio Structs: 1,43 sec, 219 Mo / 90 Mo (virt / res)
création d'instances de 1 Mio Class: 1,43 s, 219 Mo / 90 Mo (virt / res)
création de 1 million de hachages: 4,46 s, 493 Mo / 364 Mo (virt / res)
création de 1 Mio OpenStructs: 415,13 s, 2464 Mo / 2,3 Go (virt / res) # ~ 100x plus lent que les hachages
création d'OpenStructs 100K: 10,96 s, 369 Mo / 242 Mo (virt / res)
Les OpenStructs sont sloooooow et gourmands en mémoire , et ne s'adaptent pas bien aux grands ensembles de données
Créer 1 Mio OpenStructs est environ 100 fois plus lent que créer 1 Mio Hashes .
Informations très utiles pour les accros de la performance comme moi. Merci.
Bernardo Oliveira du
Je fais référence à la mise en œuvre de Ruby par Matz (MRI)
Tilo
1
Salut @Tilo, pourriez-vous partager votre code pour obtenir les résultats ci-dessus? Je veux l'utiliser pour comparer Struct & OStruct avec Hashie :: Mash. Merci.
Donny Kurnia
1
Hey @Donny, je viens de voir le vote positif et je me suis rendu compte que cela avait été mesuré en 2011 - je dois le relancer avec Ruby 2.1: P je ne sais pas si j'ai ce code, mais il devrait être simple à reproduire. J'essaierai de le réparer bientôt.
Les cas d'utilisation des deux sont assez différents.
Vous pouvez considérer la classe Struct dans Ruby 1.9 comme un équivalent à la structdéclaration en C. Dans Ruby Struct.newprend un ensemble de noms de champs comme arguments et renvoie une nouvelle classe. De même, en C, une structdéclaration prend un ensemble de champs et permet au programmeur d'utiliser le nouveau type complexe comme il le ferait avec n'importe quel type intégré.
Rubis:
Newtype=Struct.new(:data1,:data2)
n =Newtype.new
C:
typedef struct {
int data1;
char data2;} newtype;
newtype n;
La classe OpenStruct peut être comparée à une déclaration struct anonyme en C. Elle permet au programmeur de créer une instance de type complexe.
Rubis:
o =OpenStruct.new(data1:0, data2:0)
o.data1 =1
o.data2 =2
C'est une excellente réponse à la différence conceptuelle entre eux. Merci d'avoir souligné l'anonymat d'OpenStruct, j'ai l'impression que cela le rend beaucoup plus clair.
bryant
Excellente explication!
Yuri Ghensev
24
Les OpenStructs utilisent beaucoup plus de mémoire et sont plus lents que les Structs.
Mais vous savez que le test OpenStruct crée beaucoup de hachages temporaires. Je suggère un benchmark légèrement modifié - qui soutient toujours votre verdict (voir ci-dessous).
Merci pour l'exemple. Cela aide beaucoup à comprendre dans la pratique.
Ahsan
5
Jetez un œil à l'API en ce qui concerne la nouvelle méthode. On y trouve beaucoup de différences.
Personnellement, j'aime assez OpenStruct, car je n'ai pas besoin de définir la structure de l'objet au préalable, et d'ajouter simplement des éléments comme je veux. Je suppose que ce serait son principal (dés) avantage?
En utilisant le code @Robert, j'ajoute Hashie :: Mash à l'élément de référence et j'ai obtenu ce résultat:
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)
Votre référence est vraiment étrange. J'ai obtenu le résultat suivant avec ruby2.1.1 sur un mac i5: gist.github.com/nicolas-besnard/…
cappie013
Eh bien, le résultat variera entre la version ruby utilisée et le matériel utilisé pour l'exécuter. Mais le schéma est toujours le même, OpenStruct est le plus lent, Struct est le plus rapide. Hashie tombe au milieu.
Donny Kurnia
0
Pas vraiment une réponse à la question, mais une considération très importante si vous vous souciez de la performance . Veuillez noter qu'à chaque fois que vous créez une OpenStructopération, l'opération efface le cache de méthode, ce qui signifie que votre application fonctionnera plus lentement. La lenteur ou non de ne OpenStructconcerne pas seulement la façon dont cela fonctionne par lui-même, mais les implications que leur utilisation apporte à l'ensemble de l'application: https://github.com/charliesome/charlie.bz/blob/master/posts/things-that -clear-rubys-method-cache.md # openstructs
Réponses:
Avec un
OpenStruct
, vous pouvez créer arbitrairement des attributs. AStruct
, d'autre part, doit avoir ses attributs définis lorsque vous le créez. Le choix de l'un par rapport à l'autre doit être basé principalement sur la question de savoir si vous devez être en mesure d'ajouter des attributs ultérieurement.La façon de penser à eux est comme le milieu du spectre entre les hachages d'un côté et les classes de l'autre. Ils impliquent une relation plus concrète entre les données que ne le fait a
Hash
, mais ils n'ont pas les méthodes d'instance comme le ferait une classe. Un tas d'options pour une fonction, par exemple, a du sens dans un hachage; ils ne sont que vaguement liés. Un nom, une adresse e-mail et un numéro de téléphone nécessaires à une fonction peuvent être regroupés dans un fichierStruct
ouOpenStruct
. Si ce nom, cette adresse e-mail et ce numéro de téléphone ont besoin de méthodes pour fournir le nom aux formats «First Last» et «Last, First», vous devez créer une classe pour le gérer.la source
class Point < Struct.new(:x, :y); methods here; end
Point = Struct.new(:x, :y) { methods here }
. ( source ) Bien sûr,{ ... }
il peut être écrit sous forme de bloc multi-lignes (do ... end
) et, je pense, c'est la méthode préférée.Autre repère:
Pour les impatients qui veulent se faire une idée des résultats du benchmark, sans les exécuter eux-mêmes, voici la sortie du code ci-dessus (sur un MB Pro 2.4GHz i7)
la source
METTRE À JOUR:
Depuis Ruby 2.4.1, OpenStruct et Struct sont beaucoup plus rapides. Voir https://stackoverflow.com/a/43987844/128421
PRÉCÉDEMMENT:
Par souci d' exhaustivité: Struct vs Class vs Hash vs OpenStruct
Exécution d'un code similaire à celui de Burtlo, sur Ruby 1.9.2, (1 des 4 cœurs x86_64, 8 Go de RAM) [table modifiée pour aligner les colonnes]:
Les OpenStructs sont sloooooow et gourmands en mémoire , et ne s'adaptent pas bien aux grands ensembles de données
Créer 1 Mio OpenStructs est environ 100 fois plus lent que créer 1 Mio Hashes .
la source
Les cas d'utilisation des deux sont assez différents.
Vous pouvez considérer la classe Struct dans Ruby 1.9 comme un équivalent à la
struct
déclaration en C. Dans RubyStruct.new
prend un ensemble de noms de champs comme arguments et renvoie une nouvelle classe. De même, en C, unestruct
déclaration prend un ensemble de champs et permet au programmeur d'utiliser le nouveau type complexe comme il le ferait avec n'importe quel type intégré.Rubis:
C:
La classe OpenStruct peut être comparée à une déclaration struct anonyme en C. Elle permet au programmeur de créer une instance de type complexe.
Rubis:
C:
Voici quelques cas d'utilisation courants.
OpenStructs peut être utilisé pour convertir facilement les hachages en objets uniques qui répondent à toutes les clés de hachage.
Les structures peuvent être utiles pour les définitions de classes abrégées.
la source
Les OpenStructs utilisent beaucoup plus de mémoire et sont plus lents que les Structs.
Sur mon système, le code suivant s'est exécuté en 14 secondes et a consommé 1,5 Go de mémoire. Votre kilométrage peut varier:
Cela s'est terminé presque instantanément et a consommé 26,6 Mo de mémoire.
la source
Struct
:OpenStruct
:la source
Jetez un œil à l'API en ce qui concerne la nouvelle méthode. On y trouve beaucoup de différences.
Personnellement, j'aime assez OpenStruct, car je n'ai pas besoin de définir la structure de l'objet au préalable, et d'ajouter simplement des éléments comme je veux. Je suppose que ce serait son principal (dés) avantage?
la source
En utilisant le code @Robert, j'ajoute Hashie :: Mash à l'élément de référence et j'ai obtenu ce résultat:
la source
Pas vraiment une réponse à la question, mais une considération très importante si vous vous souciez de la performance . Veuillez noter qu'à chaque fois que vous créez une
OpenStruct
opération, l'opération efface le cache de méthode, ce qui signifie que votre application fonctionnera plus lentement. La lenteur ou non de neOpenStruct
concerne pas seulement la façon dont cela fonctionne par lui-même, mais les implications que leur utilisation apporte à l'ensemble de l'application: https://github.com/charliesome/charlie.bz/blob/master/posts/things-that -clear-rubys-method-cache.md # openstructsla source