2010-07-18 19 views
57

Non ho molta esperienza di programmazione. Ma, per me, Struct sembra in qualche modo simile a Hash.Quando utilizzare Struct anziché Hash in Ruby?

  • Cosa può fare lo Struct?
  • C'è qualcosa che lo Struct può fare, che Hash non può fare?

Dopo googling, il concetto di Struct è importante in C, ma non so molto di C.

risposta

75

Structs diverso dal utilizzando HashMaps nei seguenti modi (in aggiunta al modo in cui il codice sembra):

  • Una struttura ha un insieme fisso di attributi, mentre si aggiungono nuove chiavi di un hash.
  • La chiamata di un attributo che non esiste su un'istanza di una struttura causerà un NoMethodError, mentre il valore per una chiave non esistente da un hash restituirà solo zero.
  • Due istanze di diverse strutture non saranno mai uguali anche se le strutture hanno gli stessi attributi e le istanze hanno gli stessi valori (ad esempio Struct.new(:x).new(42) == Struct.new(:x).new(42) è false, mentre Foo = Struct.new(:x); Foo.new(42)==Foo.new(42) è true).
  • Procedimento to_a per le strutture restituisce una matrice di valori, mentre to_a su un hash si ottiene una matrice di valori-chiave coppie (dove "coppia" significa "due-elemento array")
  • Se Foo = Struct.new(:x, :y, :z) si può fare Foo.new(1,2,3) per creare un'istanza di Foo senza dover specificare i nomi degli attributi.

Quindi, per rispondere alla domanda: quando si desidera modellare oggetti con un insieme noto di attributi, utilizzare le strutture. Quando si desidera modellare hashmaps di uso arbitrario (ad esempio contando quante volte ogni parola si verifica in una stringa o mappando i nickname ai nomi completi ecc. Non sono sicuramente lavori per una struttura, mentre si modella una persona con un nome, un'età e un indirizzo sarebbero perfetto per Person = Struct.new(name, age, address)).

Come nota a margine: le strutture C hanno poco o nulla a che fare con le strutture ruby, quindi non lasciarti confondere da quello.

+0

I suoi altri punti sono tutti corretti (in modo +1 per questo), ma [ 'struct # == '] (http://ruby-doc.org/core/classes/Struct.html#M000890) funziona in modo diverso da quello che hai spiegato quando memorizzi il risultato di' Struct.new' invece di chiamarlo due volte con gli stessi argomenti. –

+0

@MarkRushakoff: Se faccio 'Foo = Struct.new (: x); Bar = Struct.new (: x) 'e quindi do' Foo.new (42) == Bar.new (42) 'Otterrò falso. Se faccio 'Foo.new (42) == Foo.new (42)' Avrò vero. E se leggi attentamente, è esattamente quello che ho detto (due istanze di * diverse * structs "). – sepp2k

+0

Capisco cosa intendi. Non era chiaro per me perché non lo mettevi in ​​contrasto con una spiegazione che l'uguaglianza funziona come atteso quando si utilizza lo stesso tipo di Struct –

9

Dalla documentazione Struct:

una struct è un modo conveniente per raggruppare insieme un numero di attributi, usando metodi accessor, senza dover scrivere una classe esplicita.

D'altra parte, un Hash:

Un hash è una raccolta di coppie di valori-chiave. È simile a una matrice, tranne per il fatto che l'indicizzazione viene eseguita tramite chiavi arbitrarie di qualsiasi tipo di oggetto, non un indice intero. L'ordine in cui attraversi un hash con una chiave o un valore può sembrare arbitrario e generalmente non si trova nell'ordine di inserimento.

La differenza principale è il modo in cui si accede ai dati.

ruby-1.9.1-p378 > Point = Struct.new(:x, :y) 
=> Point 
ruby-1.9.1-p378 > p = Point.new(4,5) 
=> #<struct Point x=4, y=5> 
ruby-1.9.1-p378 > p.x 
=> 4 
ruby-1.9.1-p378 > p.y 
=> 5 
ruby-1.9.1-p378 > p = {:x => 4, :y => 5} 
=> {:x=>4, :y=>5} 
ruby-1.9.1-p378 > p.x 
NoMethodError: undefined method `x' for {:x=>4, :y=>5}:Hash 
    from (irb):7 
    from /Users/mr/.rvm/rubies/ruby-1.9.1-p378/bin/irb:17:in `<main>' 
ruby-1.9.1-p378 > p[:x] 
=> 4 
ruby-1.9.1-p378 > p[:y] 
=> 5 

In breve, si potrebbe fare una nuova struttura quando si vuole una classe che è un "plain old data" structure (a scelta con l'intento di estendere con più metodi), e si dovrebbe utilizzare un hash, quando non hai bisogno di un tipo formale a tutti.

33

So che questa domanda ha avuto quasi risposta, ma sorprendentemente nessuno ha parlato di una delle maggiori differenze e dei reali vantaggi di Struct. E immagino che sia il motivo per cui somebody is still asking.

Comprendo le differenze, ma qual è il vero vantaggio dell'utilizzo di Struct su un hash, quando un hash può fare la stessa cosa ed è più semplice da gestire? Sembra che le strutture siano una specie di superfluo.

Struct è più veloce.

require 'benchmark' 

Benchmark.bm 10 do |bench| 
    bench.report "Hash: " do 
    50_000_000.times do { name: "John Smith", age: 45 } end 
    end 

    bench.report "Struct: " do 
    klass = Struct.new(:name, :age) 
    50_000_000.times do klass.new("John Smith", 45) end 
    end 

end 

# ruby 2.2.2p95 (2015-04-13 revision 50295) [x64-mingw32]. 
#     user  system  total  real 
# Hash:  22.340000 0.016000 22.356000 (24.260674) 
# Struct:  12.979000 0.000000 12.979000 (14.095455) 

# ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin11.0] 
# 
#     user  system  total  real 
# Hash:  31.980000 0.060000 32.040000 (32.039914) 
# Struct:  16.880000 0.010000 16.890000 (16.886061) 
+3

Forse è interessante: rieseguo il tuo benchmark una volta con Cygwin Ruby (2.2.3p173) e ho ottenuto per gli utenti tempi 62.462 per l'hash e 19.875. Poi l'ho ripetuto con JRuby 1.7.23 su JVM 1.7 e ottenuto 8.401 per l'hash e 8.701 per lo Struct. Mentre il vantaggio di velocità è grande su Ruby, entrambi sembrano avere la stessa velocità sotto JRuby. – user1934428

+0

Mi chiedo che consuma meno memoria. – Nakilon

8

più Una differenza principale è che si può aggiungere metodi di comportamento ad una struct.

Customer = Struct.new(:name, :address) do 

    def greeting; "Hello #{name}!" ; end 

end 

Customer.new("Dave", "123 Main").greeting # => "Hello Dave!" 
+0

Penso che questa sia una grande differenza che giustificherebbe Struct vs Hash. Dato che per convenzione Rails non dovresti avere due classi nello stesso file di ruby ​​(a causa di problemi di caricamento automatico), molte volte una Struct è un ottimo modo per creare una sostituzione di classe che funge da presentatore/decoratore. – sandre89

0

Se si desidera solo incapsulare i dati, allora un hash (o una matrice di hash) vanno bene. Se avete in programma di avere i dati di manipolare o interagire con altri dati, quindi una struttura in grado di aprire alcune interessanti possibilità:

Point = Struct.new(:x, :y) 
point_a = Point.new(0,0) 
point_b = Point.new(2,3) 

class Point 
    def distance_to another_point 
    Math.sqrt((self.x - another_point.x)**2 + (self.y - another_point.y)**2) 
    end 
end 

puts point_a.distance_to point_b 
+0

Si può fare 'class Point << Hash' e tutto sarà come lo stesso. – Nakilon