In generale, quali sono i vantaggi e gli svantaggi dell'utilizzo di OpenStruct rispetto a Struct? Che tipo di casi d'uso generali si adatterebbe a ciascuno di questi?
In generale, quali sono i vantaggi e gli svantaggi dell'utilizzo di OpenStruct rispetto a Struct? Che tipo di casi d'uso generali si adatterebbe a ciascuno di questi?
Risposte:
Con un OpenStruct
, puoi creare arbitrariamente attributi. A Struct
, invece, devono avere i suoi attributi definiti quando lo si crea. La scelta dell'una sull'altra dovrebbe basarsi principalmente sulla necessità di poter aggiungere attributi in un secondo momento.
Il modo di pensarci è come la via di mezzo tra gli hash da un lato e le classi dall'altro. Implica una relazione più concreta tra i dati rispetto a una Hash
, ma non ha i metodi di istanza come farebbe una classe. Un gruppo di opzioni per una funzione, ad esempio, ha un senso in un hash; sono solo vagamente imparentati. Un nome, un indirizzo e-mail e un numero di telefono necessari a una funzione possono essere raggruppati in un Struct
o OpenStruct
. Se quel nome, e-mail e numero di telefono necessitavano di metodi per fornire il nome in entrambi i formati "Primo ultimo" e "Ultimo, primo", allora dovresti creare una classe per gestirlo.
class Point < Struct.new(:x, :y); methods here; end
Point = Struct.new(:x, :y) { methods here }
. ( fonte ) Naturalmente, { ... }
ci può essere scritto come un blocco multi-linea ( do ... end
) e, penso, questo è il modo preferito.
Altro benchmark:
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
Per l'impaziente che vuole farsi un'idea dei risultati del benchmark, senza eseguirli da soli, ecco l'output del codice sopra (su 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)
AGGIORNARE:
A partire da Ruby 2.4.1 OpenStruct e Struct sono molto più vicini nella velocità. Vedi https://stackoverflow.com/a/43987844/128421
IN PRECEDENZA:
Per completezza: Struct vs. Class vs. Hash vs. OpenStruct
Esecuzione di codice simile a quello di burtlo, su Ruby 1.9.2, (1 su 4 core x86_64, 8 GB RAM) [tabella modificata per allineare le colonne]:
creazione di 1 Mio Struct: 1,43 sec, 219 MB / 90 MB (virt / res) creazione di 1 istanze di classe Mio: 1,43 sec, 219 MB / 90 MB (virt / res) creazione di 1 Mio Hash: 4,46 sec, 493 MB / 364 MB (virt / res) creazione di 1 Mio OpenStruct: 415.13 sec, 2464 MB / 2.3GB (virt / res) # ~ 100 volte più lento degli hash creazione di 100.000 OpenStruct: 10,96 sec, 369 MB / 242 MB (virt / res)
OpenStruct richiede molta memoria e molta memoria e non si adatta bene a grandi set di dati
La creazione di 1 Mio OpenStructs è circa 100 volte più lenta della creazione di 1 Mio Hash .
start = Time.now
collection = (1..10**6).collect do |i|
{:name => "User" , :age => 21}
end; 1
stop = Time.now
puts "#{stop - start} seconds elapsed"
I casi d'uso per i due sono abbastanza diversi.
Puoi pensare alla classe Struct in Ruby 1.9 come equivalente alla struct
dichiarazione in C. In Ruby Struct.new
accetta una serie di nomi di campo come argomenti e restituisce una nuova classe. Allo stesso modo, in C, una struct
dichiarazione accetta una serie di campi e consente al programmatore di utilizzare il nuovo tipo complesso proprio come farebbe con qualsiasi tipo incorporato.
Rubino:
Newtype = Struct.new(:data1, :data2)
n = Newtype.new
C:
typedef struct {
int data1;
char data2;
} newtype;
newtype n;
La classe OpenStruct può essere confrontata con una dichiarazione di struttura anonima in C. Permette al programmatore di creare un'istanza di un tipo complesso.
Rubino:
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;
Ecco alcuni casi d'uso comuni.
OpenStructs può essere utilizzato per convertire facilmente hash in oggetti unici che rispondono a tutte le chiavi hash.
h = { a: 1, b: 2 }
o = OpenStruct.new(h)
o.a = 1
o.b = 2
Le strutture possono essere utili per le definizioni delle classi abbreviate.
class MyClass < Struct.new(:a,:b,:c)
end
m = MyClass.new
m.a = 1
OpenStruct utilizza molta più memoria e ha prestazioni più lente rispetto a Struct.
require 'ostruct'
collection = (1..100000).collect do |index|
OpenStruct.new(:name => "User", :age => 21)
end
Sul mio sistema il seguente codice è stato eseguito in 14 secondi e ha consumato 1,5 GB di memoria. Il chilometraggio può variare:
User = Struct.new(:name, :age)
collection = (1..100000).collect do |index|
User.new("User",21)
end
Ciò è terminato quasi istantaneamente e ha consumato 26,6 MB di 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
Dai un'occhiata all'API per quanto riguarda il nuovo metodo. Molte differenze si possono trovare lì.
Personalmente, mi piace abbastanza OpenStruct, in quanto non devo prima definire la struttura dell'oggetto e aggiungere semplicemente le cose che voglio. Immagino che sarebbe il suo principale (dis) vantaggio?
Usando il codice @Robert, aggiungo Hashie :: Mash all'elemento di riferimento e ho ottenuto questo risultato:
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)
In realtà non è una risposta alla domanda, ma una considerazione molto importante se ti preoccupi delle prestazioni . Si noti che ogni volta che si crea OpenStruct
un'operazione cancella la cache del metodo, il che significa che l'applicazione funzionerà più lentamente. La lentezza o meno OpenStruct
non riguarda solo il modo in cui funziona da solo, ma le implicazioni che il loro utilizzo porta all'intera applicazione: https://github.com/charliesome/charlie.bz/blob/master/posts/things-that -clear-rubys-method-cache.md # openstructs