Come capire i simboli in Ruby


85

Nonostante abbia letto " Capire i simboli di Ruby ", sono ancora confuso dalla rappresentazione dei dati in memoria quando si tratta di usare i simboli. Se un simbolo, due dei quali contenuti in oggetti diversi, esiste nella stessa posizione di memoria, allora come mai contengono valori diversi ? Mi sarei aspettato che la stessa posizione di memoria contenesse lo stesso valore.

Questa è una citazione dal link:

A differenza delle stringhe, i simboli con lo stesso nome vengono inizializzati ed esistono in memoria solo una volta durante una sessione di ruby

Non capisco come riesca a differenziare i valori contenuti nella stessa locazione di memoria.

Considera questo esempio:

patient1 = { :ruby => "red" }
patient2 = { :ruby => "programming" }

patient1.each_key {|key| puts key.object_id.to_s}
3918094
patient2.each_key {|key| puts key.object_id.to_s}
3918094

patient1e patient2sono entrambi hash, va bene. :rubytuttavia è un simbolo. Se dovessimo produrre quanto segue:

patient1.each_key {|key| puts key.to_s}

Quindi cosa verrà prodotto? "red"o "programming"?

Dimenticando gli hash per un secondo, penso che un simbolo sia un puntatore a un valore. Le domande che ho sono:

  • Posso assegnare un valore a un simbolo?
  • Un simbolo è solo un puntatore a una variabile con un valore in essa?
  • Se i simboli sono globali, significa che un simbolo punta sempre a una cosa?

1
Produrrà ": ruby", perché stai stampando un simbolo. Se dici puts patient1[:ruby], stamperà "rosso", se dici puts patient2[:ruby], stamperà "programmazione".
ROSS

1
Un simbolo NON è un puntatore a un valore. Internamente un simbolo è solo un numero intero.
akuhn

Risposte:


62

Considera questo:

x = :sym
y = :sym
(x.__id__ == y.__id__ ) && ( :sym.__id__ == x.__id__) # => true

x = "string"
y = "string"
(x.__id__ == y.__id__ ) || ( "string".__id__ == x.__id__) # => false

Quindi, comunque crei un oggetto simbolo, finché il suo contenuto è lo stesso, farà riferimento allo stesso oggetto in memoria. Questo non è un problema perché un simbolo è un oggetto immutabile . Le stringhe sono mutabili.


(In risposta al commento sotto)

Nell'articolo originale, il valore non viene memorizzato in un simbolo, ma in un hash. Considera questo:

hash1 = { "string" => "value"}
hash2 = { "string" => "value"}

Questo crea sei oggetti nella memoria: quattro oggetti stringa e due oggetti hash.

hash1 = { :symbol => "value"}
hash2 = { :symbol => "value"}

Questo crea solo cinque oggetti in memoria: un simbolo, due stringhe e due oggetti hash.


L'esempio nel collegamento, tuttavia, mostra i simboli contenenti valori diversi , ma il simbolo ha lo stesso nome e la stessa posizione di memoria. Quando vengono emessi, hanno valori diversi , questa è la parte che non ricevo. Sicuramente dovrebbero contenere lo stesso valore?
Kezzer

1
Ho appena apportato una modifica per cercare di spiegare come sono ancora confuso. Il mio cervello non è in grado di calcolare;)
Kezzer

48
I simboli non contengono valori, sono valori. Gli hash contengono valori.
Mladen Jablanović

5
È il Hash(creato da {... => ...} nel codice) che memorizza le coppie chiave / valore, non le Symbols stesse. Le Symbols (ad esempio :symbolo :symo :ruby) sono le chiavi nelle coppie. Solo come parte di un Hash"indicano" qualcosa.
James A. Rosen

1
Il simbolo viene utilizzato come chiave nell'hash non nel valore, ecco perché possono essere diversi, è simile all'utilizzo di key1 = 'ruby' e hash1 = {key1 => 'value' ...} hash2 = { key1 => 'value2' ...}.
Joshua Olson

53

Riuscivo a ingannare i simboli quando ci pensavo in questo modo. Una stringa Ruby è un oggetto che ha una serie di metodi e proprietà. Alla gente piace usare le stringhe per le chiavi e quando la stringa viene usata per una chiave allora tutti quei metodi extra non vengono usati. Così hanno creato i simboli, che sono oggetti stringa con tutte le funzionalità rimosse, tranne quella necessaria per essere una buona chiave.

Pensa ai simboli come stringhe costanti.


2
Leggendo i post, questo probabilmente ha più senso per me. : ruby ​​è solo immagazzinato da qualche parte in memoria, se uso "ruby" da qualche parte, poi "ruby" di nuovo da qualche parte di nuovo, è solo una duplicazione. Quindi usare i simboli è un modo per ridurre la duplicazione di dati comuni. Come dici tu, stringhe costanti. Immagino che ci sia un meccanismo sottostante che ritroverà quel simbolo da usare?
Kezzer

@Kezzer Questa risposta è davvero buona e mi sembra giusta, ma il tuo commento dice qualcosa di diverso ed è sbagliato o fuorviante, il tuo commento parla di duplicazione di dati con stringhe e questo è il motivo dei simboli, è sbagliato o fuorviante. il simbolo più volte non occuperà più spazio di memoria, ma puoi averlo anche per le stringhe in molti linguaggi es. alcuni linguaggi di programmazione se scrivi "abc" e altrove "abc" il compilatore vede cheèla stessa stringa di valore e la memorizza nello stesso posto rendendolo lo stesso oggetto, che si chiama string interning e c # lo fa.
barlop

Quindi è fondamentalmente una versione incredibilmente leggera di una corda?
stevec,

34

Il simbolo :rubynon contiene "red"o "programming". Il simbolo :rubyè solo il simbolo :ruby. Sono i tuoi hash patient1e patient2ognuno di essi contiene quei valori, puntati in ogni caso dalla stessa chiave.

Pensaci in questo modo: se vai in soggiorno la mattina di Natale e vedi due scatole con un'etichetta che dice "Kezzer". Su ha i calzini e l'altro ha il carbone. Non ti confonderai chiedendoti come "Kezzer" possa contenere sia i calzini che il carbone, anche se è lo stesso nome. Perché il nome non contiene i regali (schifosi). Sta solo indicando loro. Allo stesso modo, :rubynon contiene i valori nel tuo hash, li punta solo.


2
Questa risposta ha perfettamente senso.
Vass

Sembra un miscuglio totale di hash e simboli. Un simbolo non punta a un valore, se vuoi dire che lo fa quando è in un hash, beh, potrebbe essere discutibile, ma un simbolo non deve essere necessariamente in un hash. Puoi dire che mystring = :steveT il simbolo non punta a nulla. Una chiave in un hash ha un valore associato e la chiave potrebbe essere un simbolo. Ma un simbolo non deve essere necessariamente in un hash.
barlop

27

Potresti presumere che la dichiarazione che hai fatto definisca il valore di un simbolo come qualcosa di diverso da quello che è. In effetti, un simbolo è solo un valore stringa "interiorizzato" che rimane costante. È perché vengono memorizzati utilizzando un semplice identificatore intero che vengono spesso utilizzati in quanto è più efficiente rispetto alla gestione di un numero elevato di stringhe di lunghezza variabile.

Prendi il caso del tuo esempio:

patient1 = { :ruby => "red" }

Dovrebbe essere letto come: "dichiara una variabile paziente1 e definiscila come un hash, e in questo memorizza il valore 'rosso' sotto la chiave (simbolo 'rubino')"

Un altro modo per scrivere questo è:

patient1 = Hash.new
patient1[:ruby] = 'red'

puts patient1[:ruby]
# 'red'

Mentre stai facendo un incarico, non sorprende che il risultato che ottieni sia identico a quello con cui lo hai assegnato in primo luogo.

Il concetto di simbolo può essere un po 'confuso in quanto non è una caratteristica della maggior parte delle altre lingue.

Ogni oggetto String è distinto anche se i valori sono identici:

[ "foo", "foo", "foo", "bar", "bar", "bar" ].each do |v|
  puts v.inspect + ' ' + v.object_id.to_s
end

# "foo" 2148099960
# "foo" 2148099940
# "foo" 2148099920
# "bar" 2148099900
# "bar" 2148099880
# "bar" 2148099860

Ogni Simbolo con lo stesso valore fa riferimento allo stesso oggetto:

[ :foo, :foo, :foo, :bar, :bar, :bar ].each do |v|
  puts v.inspect + ' ' + v.object_id.to_s
end

# :foo 228508
# :foo 228508
# :foo 228508
# :bar 228668
# :bar 228668
# :bar 228668

La conversione di stringhe in simboli mappa valori identici allo stesso simbolo univoco:

[ "foo", "foo", "foo", "bar", "bar", "bar" ].each do |v|
  v = v.to_sym
  puts v.inspect + ' ' + v.object_id.to_s
end

# :foo 228508
# :foo 228508
# :foo 228508
# :bar 228668
# :bar 228668
# :bar 228668

Allo stesso modo, la conversione da Symbol a String crea ogni volta una stringa distinta:

[ :foo, :foo, :foo, :bar, :bar, :bar ].each do |v|
  v = v.to_s
  puts v.inspect + ' ' + v.object_id.to_s
end

# "foo" 2148097820
# "foo" 2148097700
# "foo" 2148097580
# "bar" 2148097460
# "bar" 2148097340
# "bar" 2148097220

Puoi pensare ai valori dei simboli come disegnati da una tabella hash interna e puoi vedere tutti i valori che sono stati codificati in simboli utilizzando una semplice chiamata al metodo:

Symbol.all_values

# => [:RUBY_PATCHLEVEL, :vi_editing_mode, :Separator, :TkLSHFT, :one?, :setuid?, :auto_indent_mode, :setregid, :back, :Fail, :RET, :member?, :TkOp, :AP_NAME, :readbyte, :suspend_context, :oct, :store, :WNOHANG, :@seek, :autoload, :rest, :IN_INPUT, :close_read, :type, :filename_quote_characters=, ...

Man mano che si definiscono nuovi simboli tramite la notazione dei due punti o utilizzando .to_sym, questa tabella crescerà.


17

I simboli non sono puntatori. Non contengono valori. I simboli lo sono semplicemente . :rubyè il simbolo :rubye questo è tutto quello che c'è da fare. Non contiene un valore, non fa nulla, esiste solo come simbolo :ruby. Il simbolo :rubyè un valore proprio come lo è il numero 1. Non punta a un altro valore più di quanto non faccia il numero 1.


13
patient1.each_key {|key| puts key.to_s}

Quindi cosa verrà prodotto? "rosso" o "programmazione"?

Nessuno dei due produrrà "ruby".

Stai confondendo simboli e hash. Non sono correlati, ma sono utili insieme. Il simbolo in questione è :ruby; non ha nulla a che fare con i valori nell'hash, e la sua rappresentazione interna intera sarà sempre la stessa, e il suo "valore" (quando convertito in una stringa) sarà sempre "rubino".


10

In breve

I simboli risolvono il problema della creazione di rappresentazioni leggibili e immutabili che hanno anche il vantaggio di essere più semplici da cercare per il runtime rispetto alle stringhe. Consideralo come un nome o un'etichetta che può essere riutilizzato.

Perché: il rosso è meglio di "rosso"

Nei linguaggi dinamici orientati agli oggetti si creano strutture di dati complesse e annidate con riferimenti leggibili. L' hash è un caso d'uso comune in cui si associano i valori a chiavi univoche, almeno univoche per ciascuna istanza. Non puoi avere più di una chiave "rossa" per hash.

Tuttavia, sarebbe più efficiente del processore utilizzare un indice numerico invece di chiavi stringa. Quindi i simboli sono stati introdotti come compromesso tra velocità e leggibilità. I simboli si risolvono molto più facilmente rispetto alla stringa equivalente. Essendo leggibili dall'uomo e facili per il runtime, i simboli sono un'aggiunta ideale a un linguaggio dinamico.

Benefici

Poiché i simboli non sono modificabili, possono essere condivisi in tutto il runtime. Se due istanze di hash hanno una comune esigenza lessicografica o semantica per un elemento rosso, il simbolo: red userebbe all'incirca metà della memoria che la stringa "red" avrebbe richiesto per due hash.

Poiché: il rosso si risolve sempre nella stessa posizione in memoria, può essere riutilizzato in un centinaio di istanze di hash quasi senza aumento della memoria, mentre l'uso di "rosso" aggiungerà un costo di memoria poiché ogni istanza di hash dovrebbe memorizzare la stringa modificabile su creazione.

Non sono sicuro di come Ruby implementi effettivamente simboli / stringhe, ma chiaramente un simbolo offre meno overhead di implementazione nel runtime poiché è una rappresentazione fissa. I simboli Plus richiedono un carattere in meno per essere digitato rispetto a una stringa tra virgolette e meno battitura è l'eterna ricerca dei veri Rubyists.

Sommario

Con un simbolo come: rosso si ottiene la leggibilità della rappresentazione di stringa con meno overhead a causa del costo delle operazioni di confronto delle stringhe e della necessità di memorizzare ogni istanza di stringa in memoria.


4

Consiglierei di leggere l' articolo di Wikipedia sulle tabelle hash : penso che ti aiuterà a capire cosa {:ruby => "red"}significa veramente.

Un altro esercizio che potrebbe aiutarti a comprendere la situazione: considera {1 => "red"}. Semanticamente, questo non significa "impostare il valore di 1a "red"", cosa impossibile in Ruby. Piuttosto, significa "creare un oggetto Hash e memorizzare il valore "red"per la chiave 1.


3
patient1 = { :ruby => "red" }
patient2 = { :ruby => "programming" }

patient1.each_key {|key| puts key.object_id.to_s}
3918094
patient2.each_key {|key| puts key.object_id.to_s}
3918094

patient1e patient2sono entrambi hash, va bene. :rubytuttavia è un simbolo. Se dovessimo produrre quanto segue:

patient1.each_key {|key| puts key.to_s}

Quindi cosa verrà prodotto? "rosso" o "programmazione"?

Nessuno dei due, ovviamente. L'output sarà ruby. Che, a proposito, avresti potuto scoprire in meno tempo di quanto hai impiegato per digitare la domanda, semplicemente digitandola in IRB.

Perché dovrebbe essere redo programming? I simboli si valutano sempre da soli. Il valore del simbolo :rubyè il simbolo :rubystesso e la rappresentazione di stringa del simbolo :rubyè il valore di stringa "ruby".

[BTW: putsconverte sempre i suoi argomenti in stringhe, comunque. Non c'è bisogno di chiamarlo to_s.]


Non ho IRB sulla macchina corrente, né sarei in grado di installarlo, quindi perché, quindi mi scuso per questo.
Kezzer

2
@ Kezzer: Nessun problema, ero solo curioso. A volte ti seppellisci così profondamente in un problema che non riesci nemmeno più a vedere le cose più semplici. Quando ho praticamente tagliato e incollato la tua domanda in IRB, mi sono chiesto: "perché non l'ha fatto lui stesso?" E non preoccuparti, non sei il primo (né sarai l'ultimo) a chiedere "cosa fa questa stampa" quando la risposta è "eseguila e basta!" BTW: ecco il tuo IRB istantaneo, ovunque e in qualsiasi momento, nessuna installazione necessaria: TryRuby.Org o Ruby-Versions.Net ti dà accesso SSH a tutte le versioni di MRI mai rilasciate + YARV + JRuby + Rubinius + REE.
Jörg W Mittag

Grazie, ci sto solo giocando ora. Sono ancora un po 'confuso, quindi riesco a ripeterlo.
Kezzer

0

Sono nuovo su Ruby, ma penso (spero?) Che questo sia un modo semplice per vederlo ...

Un simbolo non è una variabile o una costante. Non rappresenta o indica un valore. Un simbolo È un valore.

Tutto ciò che è, è una stringa senza l'overhead dell'oggetto. Il testo e solo il testo.

Così questo:

"hellobuddy"

È lo stesso di questo:

:hellobuddy

Tranne che non puoi fare, ad esempio,: hellobuddy.upcase. È il valore della stringa e SOLO il valore della stringa.

Allo stesso modo, questo:

greeting =>"hellobuddy"

È lo stesso di questo:

greeting => :hellobuddy

Ma, ancora una volta, senza l'overhead dell'oggetto stringa.


-1

Un modo semplice per capire questo è pensare, "e se usassi una stringa piuttosto che un simbolo?

patient1 = { "ruby" => "red" }
patient2 = { "ruby" => "programming" }

Non è affatto confuso, giusto? Stai usando "ruby" come chiave in un hash .

"ruby"è una stringa letterale, quindi questo è il valore. L'indirizzo di memoria, o il puntatore, non è disponibile. Ogni volta che invocate "ruby", ne create una nuova istanza, ovvero una nuova cella di memoria contenente lo stesso valore - "ruby".

L'hash quindi va "qual è il mio valore chiave? Oh, lo è "ruby". Quindi mappa quel valore su" rosso "o" programmazione ". In altre parole, :rubynon dereferenzia a "red"o "programming". L'hash mappa :ruby a "red"o "programming".

Confrontalo con se usiamo simboli

patient1 = { :ruby => "red" }
patient2 = { :ruby => "programming" }

Il valore di :rubyè anche "ruby", in modo efficace.

Perché? Perché i simboli sono essenzialmente costanti di stringa . Le costanti non hanno più istanze. È lo stesso indirizzo di memoria. E un indirizzo di memoria ha un certo valore, una volta dereferenziato. Per i simboli, il nome del puntatore è il simbolo e il valore dereferenziato è una stringa, che corrisponde al nome del simbolo, in questo caso "ruby",.

Quando in un hash, non stai usando il simbolo, il puntatore, ma il valore differito. Non stai usando :ruby, ma "ruby". L'hash quindi cerca la chiave "ruby", il valore è "red"o "programming", a seconda di come hai definito l'hash.

Il cambiamento di paradigma e il concetto da portare a casa è che il valore di un simbolo è un concetto completamente separato da un valore mappato da un hash, data una chiave di quell'hash.


qual è l'errore o l'errore in questa spiegazione, downvoters? curioso per il bene dell'apprendimento.
ahnbizcad

solo perché un'analogia può essere disgustosa per alcuni, non significa che sia difettosa.
ahnbizcad

2
Non vedo errori, necessariamente, ma è estremamente difficile definire cosa stai cercando di dire in questa risposta.
Reverse Engineered

x è interpretato e concettualizzato in modo diverso in base al contesto / entità che esegue l'interpretazione. abbastanza semplice.
ahnbizcad

revisionato la risposta. Grazie per il feedback.
ahnbizcad
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.