Risposte:
TL; DR:
L'uso dei simboli non solo consente di risparmiare tempo durante i confronti, ma consente anche di risparmiare memoria, poiché vengono memorizzati solo una volta.
I simboli di Ruby sono immutabili (non modificabili), il che rende molto più facile la ricerca di qualcosa
Risposta breve (ish):
L'uso dei simboli non solo consente di risparmiare tempo durante i confronti, ma consente anche di risparmiare memoria, poiché vengono memorizzati solo una volta.
I simboli in Ruby sono fondamentalmente "stringhe immutabili" . Ciò significa che non possono essere modificati e implica che lo stesso simbolo, quando viene fatto riferimento più volte nel codice sorgente, viene sempre memorizzato come la stessa entità, ad esempio ha lo stesso ID oggetto .
Le stringhe invece sono mutabili , possono essere cambiate in qualsiasi momento. Ciò implica che Ruby deve archiviare ogni stringa menzionata nel codice sorgente in un'entità separata, ad esempio se si dispone di una stringa "nome" più volte menzionata nel codice sorgente, Ruby deve archiviarle tutte in oggetti String separati, poiché esse potrebbe cambiare in seguito (questa è la natura di una stringa Ruby).
Se usi una stringa come chiave hash, Ruby deve valutare la stringa e guardarne il contenuto (e calcolare una funzione hash su quella) e confrontare il risultato con i valori (hash) delle chiavi che sono già memorizzate nell'hash .
Se usi un simbolo come chiave hash, è implicito che è immutabile, quindi Ruby può fondamentalmente fare un confronto tra la (funzione hash dell'id oggetto) e l'id (hash) degli oggetti delle chiavi che sono già memorizzati in l'hash. (più veloce)
Unico inconveniente: ogni simbolo consuma uno slot nella tabella dei simboli dell'interprete Ruby, che non viene mai rilasciato. I simboli non vengono mai raccolti nella spazzatura. Quindi un caso angolare è quando hai un gran numero di simboli (ad esempio quelli generati automaticamente). In tal caso, dovresti valutare in che modo influisce sulla dimensione del tuo interprete Ruby.
Appunti:
Se si effettuano confronti di stringhe, Ruby può confrontare i simboli semplicemente per i loro ID oggetto, senza doverli valutare. È molto più veloce rispetto al confronto di stringhe, che devono essere valutate.
Se accedi a un hash, Ruby applica sempre una funzione hash per calcolare una "chiave hash" da qualunque chiave tu usi. Puoi immaginare qualcosa come un hash MD5. E poi Ruby confronta quelle "chiavi con hash" una con l'altra.
Risposta lunga:
Il motivo è l'efficienza, con più guadagni su una stringa:
O(n)
per stringhe e costante per simboli.Inoltre, Ruby 1.9 ha introdotto una sintassi semplificata solo per l'hash con i simboli (ad es. h.merge(foo: 42, bar: 6)
) E Ruby 2.0 ha argomenti per le parole chiave che funzionano solo per i simboli.
Note :
1) Potresti essere sorpreso di apprendere che Ruby tratta le String
chiavi in modo diverso rispetto a qualsiasi altro tipo. Infatti:
s = "foo"
h = {}
h[s] = "bar"
s.upcase!
h.rehash # must be called whenever a key changes!
h[s] # => nil, not "bar"
h.keys
h.keys.first.upcase! # => TypeError: can't modify frozen string
Solo per le chiavi di stringa, Ruby utilizzerà una copia bloccata anziché l'oggetto stesso.
2) Le lettere "b", "a" e "r" sono memorizzate una sola volta per tutte le occorrenze di :bar
un programma. Prima di Ruby 2.2, era una cattiva idea creare costantemente nuovi Symbols
che non fossero mai riutilizzati, poiché sarebbero rimasti per sempre nella tabella di ricerca globale di Symbol. Ruby 2.2 li raccoglierà spazzatura, quindi non preoccuparti.
3) In realtà, calcolare l'hash per un simbolo non ha richiesto tempo in Ruby 1.8.x, poiché l'ID oggetto è stato usato direttamente:
:bar.object_id == :bar.hash # => true in Ruby 1.8.7
In Ruby 1.9.x, questo è cambiato quando gli hash cambiano da una sessione all'altra (inclusi quelli di Symbols
):
:bar.hash # => some number that will be different next time Ruby 1.9 is ran
Ri: qual è il vantaggio rispetto all'utilizzo di una stringa?
Ricerche di valori (molto) leggermente più veloci poiché l'hashing di un simbolo equivale all'hash di un numero intero rispetto all'hash di una stringa.
Svantaggio: consuma uno slot nella tabella dei simboli del programma che non viene mai rilasciato.
Sarei molto interessato a un follow-up per quanto riguarda le stringhe congelate introdotte in Ruby 2.x.
Quando hai a che fare con numerose stringhe provenienti da un input di testo (sto pensando a parametri HTTP o payload, ad esempio tramite Rack), è molto più facile usare le stringhe ovunque.
Quando hai a che fare con dozzine di loro ma non cambiano mai (se sono il "vocabolario" della tua attività), mi piace pensare che congelarli possa fare la differenza. Non ho ancora fatto alcun benchmark, ma suppongo che sarebbe vicino alla performance dei simboli.