Ruby Koans: Perché convertire un elenco di simboli in stringhe


86

Mi riferisco a questo test in about_symbols.rb in Ruby Koans https://github.com/edgecase/ruby_koans/blob/master/src/about_symbols.rb#L26

def test_method_names_become_symbols
  symbols_as_strings = Symbol.all_symbols.map { |x| x.to_s }
  assert_equal true, symbols_as_strings.include?("test_method_names_become_symbols")
end


  # THINK ABOUT IT:
  #
  # Why do we convert the list of symbols to strings and then compare
  # against the string value rather than against symbols?

Perché esattamente dobbiamo prima convertire quella lista in stringhe?

Risposte:


112

Questo ha a che fare con il funzionamento dei simboli. Per ogni simbolo, ne esiste effettivamente uno solo. Dietro le quinte, un simbolo è solo un numero a cui fa riferimento un nome (che inizia con i due punti). Pertanto, quando si confronta l'uguaglianza di due simboli, si confronta l'identità dell'oggetto e non il contenuto dell'identificatore che si riferisce a questo simbolo.

Se dovessi fare il semplice test : test == "test" , sarà falso. Quindi, se dovessi raccogliere tutti i simboli definiti finora in un array, dovresti convertirli in stringhe prima di confrontarli. Non puoi farlo nel modo opposto (converti prima la stringa che vuoi confrontare in un simbolo) perché farlo creerebbe la singola istanza di quel simbolo e "inquinerebbe" la tua lista con il simbolo che stai testando per l'esistenza.

Spero possa aiutare. Questo è un po 'strano, perché devi testare la presenza di un simbolo senza crearlo accidentalmente durante il test. Di solito non vedi un codice del genere.


2
Nota che un modo migliore per farlo in sicurezza è assegnare l'output di Symbol.all_symbolsa una variabile, quindi testare l'inclusione. I simboli sono più veloci al confronto e stai evitando di convertire migliaia di simboli in stringhe.
coreyward

4
Questo ha ancora il problema di creare il simbolo che non può essere distrutto. Eventuali test futuri per quel simbolo saranno rovinati. Ma questo è solo un Koan, non deve avere molto senso o essere veloce, basta dimostrare come funzionano i simboli.
Ruby

2
Questa risposta non funziona per me. Se stiamo cercando l'esistenza di un simbolo, perché stiamo specificando un argomento stringa a include?Se lo specificassimo :test_method_names_become_symbolsnon dovremmo convertire tutti quei simboli in stringhe.
Isaac Rabinovitch

3
Isaac, a causa del problema individuato, ovvero che specificando :test_method_names_become_symbolsnel confronto lo creerà, quindi il confronto sarà sempre vero. Convertendo all_symbolsin stringhe e confrontando stringhe, possiamo distinguere se il simbolo esisteva prima del confronto.
Stephen

3
Così tanto non capisco. Se faccio >> array_of_symbols = Symbol.all_symbols, quindi >> array_of_symbols.include? (: Not_yet_used), ottengo falso e divento vero per qualcosa che è stato definito, quindi non capisco perché la conversione in stringhe sia necessaria .
codenoob

75

Perché se lo fai:

assert_equal true, all_symbols.include?(:test_method_names_become_symbols)

Potrebbe (a seconda dell'implementazione di ruby) essere automaticamente vero, perché :test_method_names_become_symbolscrea il simbolo. Vedi questa segnalazione di bug .


1
Quindi la risposta accettata è quella sbagliata (o almeno così mi sembra).
Isaac Rabinovitch

3
Isaac, l'altra risposta non è sbagliata ma non è spiegata in modo conciso. di sicuro. Ad ogni modo, puoi verificare cosa dice Andrew con quanto segue: assert_equal true, Symbol.all_symbols.include? (: Abcdef) Questo passerà sempre (almeno, lo fa per me) indipendentemente dal simbolo. Immagino che una lezione sia, non provare a usare la presenza / assenza di simboli come una bandiera booleana.
Stephen

1
Guardando questa domanda, che ancora mi interessa, 2 anni dopo, apprezzo molto questa risposta e i commenti. Penso che Isaac abbia ragione questa è la risposta che spiega più chiaramente perché la conversione in stringhe può essere la strada da percorrere, anche se penso che potresti inserire il passaggio intermedio (memorizzare tutti i simboli prima del confronto) per farlo funzionare meglio.
codenoob

4

Entrambe le risposte sopra sono corrette, ma alla luce della domanda di Karthik sopra ho pensato di pubblicare un test che illustri come si potrebbe passare accuratamente un simbolo al includemetodo

def test_you_create_a_new_symbol_in_the_test
  array_of_symbols = []
  array_of_symbols << Symbol.all_symbols
  all_symbols = Symbol.all_symbols.map {|x| x}
  assert_equal false, array_of_symbols.include?(:this_should_not_be_in_the_symbols_collection)  #this works because we stored all symbols in an array before creating the symbol :this_should_not_be_in_the_symbols_collection in the test
  assert_equal true, all_symbols.include?(:this_also_should_not_be_in_the_symbols_collection) #This is the case noted in previous answers...here we've created a new symbol (:this_also_should_not_be_in_the_symbols_collection) in the test and then mapped all the symbols for comparison. Since we created the symbol before querying all_symbols, this test passes.
end

Una nota aggiuntiva sui Koan: fai uso di putsistruzioni e test personalizzati se non capisci nulla. Ad esempio, se vedi:

string = "the:rain:in:spain"
words = string.split(/:/)

e non hai idea di cosa wordspotrebbe essere, aggiungi la riga

puts words

ed esegui rakedalla riga di comando. Allo stesso modo, test come quello che ho aggiunto sopra possono essere utili in termini di comprensione di alcune delle sfumature di Ruby.


Guardando questa domanda, che ancora mi interessa, 2 anni dopo, apprezzo molto questa risposta e i commenti.
codenoob
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.