Se ci sono almeno due istanze della stessa stringa nel mio script, dovrei invece usare un simbolo?
Se ci sono almeno due istanze della stessa stringa nel mio script, dovrei invece usare un simbolo?
Risposte:
Una semplice regola pratica è usare i simboli ogni volta che hai bisogno di identificatori interni. Per Ruby <2.2 usa i simboli solo quando non sono generati dinamicamente, per evitare perdite di memoria.
L'unico motivo per non utilizzarli per gli identificatori generati dinamicamente è a causa di problemi di memoria.
Questa domanda è molto comune perché molti linguaggi di programmazione non hanno simboli, solo stringhe e quindi le stringhe vengono utilizzate anche come identificatori nel codice. Si dovrebbe essere preoccuparsi di ciò che i simboli sono destinate ad essere , non solo quando si dovrebbe usare simboli . I simboli devono essere identificatori. Se segui questa filosofia, è probabile che farai le cose per bene.
Esistono molte differenze tra l'implementazione di simboli e stringhe. La cosa più importante dei simboli è che sono immutabili . Ciò significa che non cambieranno mai il loro valore. Per questo motivo, i simboli vengono istanziati più velocemente delle stringhe e anche alcune operazioni come il confronto di due simboli sono più veloci.
Il fatto che un simbolo sia immutabile consente a Ruby di utilizzare lo stesso oggetto ogni volta che fai riferimento al simbolo, risparmiando memoria. Quindi ogni volta che l'interprete legge :my_key
può prenderlo dalla memoria invece di istanziarlo di nuovo. È meno costoso dell'inizializzazione ogni volta di una nuova stringa.
È possibile ottenere un elenco di tutti i simboli già istanziati con il comando Symbol.all_symbols
:
symbols_count = Symbol.all_symbols.count # all_symbols is an array with all
# instantiated symbols.
a = :one
puts a.object_id
# prints 167778
a = :two
puts a.object_id
# prints 167858
a = :one
puts a.object_id
# prints 167778 again - the same object_id from the first time!
puts Symbol.all_symbols.count - symbols_count
# prints 2, the two objects we created.
Per le versioni di Ruby precedenti alla 2.2, una volta che un simbolo viene istanziato, questa memoria non sarà mai più libera . L'unico modo per liberare la memoria è riavviare l'applicazione. Quindi i simboli sono anche una delle principali cause di perdite di memoria se utilizzati in modo errato. Il modo più semplice per generare una perdita di memoria è utilizzare il metodo to_sym
sui dati di input dell'utente, poiché questi dati cambieranno sempre, una nuova parte della memoria verrà utilizzata per sempre nell'istanza software. Ruby 2.2 ha introdotto il simbolo garbage collector , che libera i simboli generati dinamicamente, quindi le perdite di memoria generate dalla creazione di simboli dinamicamente non sono più un problema.
Rispondere alla tua domanda:
È vero che devo usare un simbolo invece di una stringa se ci sono almeno due stringhe uguali nella mia applicazione o script?
Se quello che stai cercando è un identificatore da utilizzare internamente al tuo codice, dovresti usare i simboli. Se stai stampando l'output, dovresti usare le stringhe, anche se appare più di una volta, anche allocando due oggetti diversi in memoria.
Ecco il ragionamento:
@AlanDert: se uso molte volte qualcosa come% input {type:: checkbox} nel codice haml, cosa dovrei usare come checkbox?
Io si.
@AlanDert: Ma per stampare un simbolo su una pagina html, dovrebbe essere convertito in stringa, no? qual è il punto di usarlo allora?
Qual è il tipo di input? Un identificatore del tipo di input che vuoi usare o qualcosa che vuoi mostrare all'utente?
È vero che a un certo punto diventerà codice HTML, ma nel momento in cui stai scrivendo quella riga del tuo codice, significa essere un identificatore: identifica il tipo di campo di input di cui hai bisogno. Pertanto, viene utilizzato più e più volte nel codice e ha sempre la stessa "stringa" di caratteri dell'identificatore e non genererà una perdita di memoria.
Detto questo, perché non valutiamo i dati per vedere se le stringhe sono più veloci?
Questo è un semplice benchmark che ho creato per questo:
require 'benchmark'
require 'haml'
str = Benchmark.measure do
10_000.times do
Haml::Engine.new('%input{type: "checkbox"}').render
end
end.total
sym = Benchmark.measure do
10_000.times do
Haml::Engine.new('%input{type: :checkbox}').render
end
end.total
puts "String: " + str.to_s
puts "Symbol: " + sym.to_s
Tre uscite:
# first time
String: 5.14
Symbol: 5.07
#second
String: 5.29
Symbol: 5.050000000000001
#third
String: 4.7700000000000005
Symbol: 4.68
Quindi l'uso di simboli è in realtà un po 'più veloce rispetto all'uso delle stringhe. Perché? Dipende dal modo in cui viene implementato HAML. Avrei bisogno di modificare un po 'il codice HAML per vedere, ma se continui a usare i simboli nel concetto di identificatore, la tua applicazione sarà più veloce e affidabile. Quando le domande colpiscono, confrontale e ottieni le tue risposte.
In parole povere, un simbolo è un nome, composto da caratteri, ma immutabile. Una stringa, al contrario, è un contenitore ordinato di caratteri, il cui contenuto può cambiare.
Per confrontare due stringhe, abbiamo potenzialmente bisogno di guardare ogni carattere. Per due stringhe di lunghezza N, ciò richiederà confronti N + 1 (che gli informatici chiamano "tempo O (N)").
def string_comp str1, str2
return false if str1.length != str2.length
for i in 0...str1.length
return false if str1[i] != str2[i]
end
return true
end
string_comp "foo", "foo"
Ma poiché ogni aspetto di: foo si riferisce allo stesso oggetto, possiamo confrontare i simboli guardando gli ID oggetto. Possiamo farlo con un unico confronto (che gli informatici chiamano "O (1) time").
def symbol_comp sym1, sym2
sym1.object_id == sym2.object_id
end
symbol_comp :foo, :foo
In C ++, possiamo usare "enumerazioni" per rappresentare famiglie di costanti correlate:
enum BugStatus { OPEN, CLOSED };
BugStatus original_status = OPEN;
BugStatus current_status = CLOSED;
Ma poiché Ruby è un linguaggio dinamico, non ci preoccupiamo di dichiarare un tipo BugStatus o di tenere traccia dei valori legali. Invece, rappresentiamo i valori di enumerazione come simboli:
original_status = :open
current_status = :closed
3.Un simbolo Ruby è un nome univoco e costante
In Ruby, possiamo cambiare il contenuto di una stringa:
"foo"[0] = ?b # "boo"
Ma non possiamo modificare il contenuto di un simbolo:
:foo[0] = ?b # Raises an error
Quando si passano gli argomenti delle parole chiave a una funzione Ruby, si specificano le parole chiave utilizzando i simboli:
# Build a URL for 'bug' using Rails.
url_for :controller => 'bug',
:action => 'show',
:id => bug.id
In genere, utilizzeremo i simboli per rappresentare le chiavi di una tabella hash:
options = {}
options[:auto_save] = true
options[:show_comments] = false
Ecco un bel benchmark tra stringhe e simboli che ho trovato alla codecademy:
require 'benchmark'
string_AZ = Hash[("a".."z").to_a.zip((1..26).to_a)]
symbol_AZ = Hash[(:a..:z).to_a.zip((1..26).to_a)]
string_time = Benchmark.realtime do
1000_000.times { string_AZ["r"] }
end
symbol_time = Benchmark.realtime do
1000_000.times { symbol_AZ[:r] }
end
puts "String time: #{string_time} seconds."
puts "Symbol time: #{symbol_time} seconds."
L'output è:
String time: 0.21983 seconds.
Symbol time: 0.087873 seconds.
utilizzare i simboli come identificatori di chiavi hash
{key: "value"}
i simboli consentono di chiamare il metodo in un ordine diverso
def write (file :, data :, mode: "ascii") # rimosso per brevità fine scrittura (dati: 123, file: "test.txt")
congelare per conservare come una stringa e risparmiare memoria
label = 'My Label'.freeze
/
(dopostrings
) dal collegamento. Eccolo: www.reactive.io/tips/2009/01/11/the-difference-between-ruby- symbols-and-strings