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?
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?
Réponses:
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 Struct
ou 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.
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:
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
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.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)
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]:
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 .
start = Time.now
collection = (1..10**6).collect do |i|
{:name => "User" , :age => 21}
end; 1
stop = Time.now
puts "#{stop - start} seconds elapsed"
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 Ruby Struct.new
prend un ensemble de noms de champs comme arguments et renvoie une nouvelle classe. De même, en C, une struct
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:
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:
struct {
int data1;
char data2;
} o;
o.data1 = 1;
o.data2 = 2;
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.
h = { a: 1, b: 2 }
o = OpenStruct.new(h)
o.a = 1
o.b = 2
Les structures peuvent être utiles pour les définitions de classes abrégées.
class MyClass < Struct.new(:a,:b,:c)
end
m = MyClass.new
m.a = 1
Les OpenStructs utilisent beaucoup plus de mémoire et sont plus lents que les Structs.
require 'ostruct'
collection = (1..100000).collect do |index|
OpenStruct.new(:name => "User", :age => 21)
end
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:
User = Struct.new(:name, :age)
collection = (1..100000).collect do |index|
User.new("User",21)
end
Cela s'est terminé presque instantanément et a consommé 26,6 Mo de mémoire.
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
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.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)
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 ne OpenStruct
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 # openstructs