Ruby Koans: Warum die Liste der Symbole in Zeichenfolgen konvertieren?

84

Ich beziehe mich auf diesen 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?

Warum genau müssen wir diese Liste zuerst in Zeichenfolgen konvertieren?

Codercode
quelle

Antworten:

108

Dies hängt damit zusammen, wie Symbole funktionieren. Für jedes Symbol existiert tatsächlich nur eines davon. Hinter den Kulissen ist ein Symbol nur eine Zahl, auf die ein Name verweist (beginnend mit einem Doppelpunkt). Wenn Sie also die Gleichheit zweier Symbole vergleichen, vergleichen Sie die Objektidentität und nicht den Inhalt des Bezeichners, der auf dieses Symbol verweist.

Wenn Sie den einfachen Test durchführen : test == "test" , ist er falsch. Wenn Sie also alle bisher definierten Symbole in einem Array zusammenfassen möchten, müssen Sie sie zuerst in Zeichenfolgen konvertieren, bevor Sie sie vergleichen können. Sie können dies nicht umgekehrt tun (konvertieren Sie die Zeichenfolge, die Sie zuerst vergleichen möchten, in ein Symbol), da dadurch die einzelne Instanz dieses Symbols erstellt und Ihre Liste mit dem Symbol "verschmutzt" wird, das Sie auf Existenz testen.

Hoffentlich hilft das. Dies ist etwas seltsam, da Sie das Vorhandensein eines Symbols testen müssen, ohne dieses Symbol während des Tests versehentlich zu erstellen. Normalerweise sehen Sie keinen solchen Code.

AboutRuby
quelle
2
Beachten Sie, dass eine bessere Möglichkeit, dies sicher zu tun, darin besteht, die Ausgabe Symbol.all_symbolseiner Variablen zuzuweisen und dann auf Aufnahme zu testen. Symbole sind im Vergleich schneller und Sie vermeiden, Tausende von Symbolen in Zeichenfolgen umzuwandeln.
Coreyward
4
Das hat immer noch das Problem, ein Symbol zu erstellen, das nicht zerstört werden kann. Alle zukünftigen Tests für dieses Symbol werden ruiniert. Aber dies ist nur ein Koan, es muss nicht viel Sinn machen oder schnell sein, sondern nur zeigen, wie Symbole funktionieren.
AboutRuby
2
Diese Antwort funktioniert bei mir nicht. Wenn wir nach der Existenz eines Symbols suchen, warum geben wir dann ein Zeichenfolgenargument an? include?Wenn wir angeben :test_method_names_become_symbols, müssten wir nicht alle diese Symbole in Zeichenfolgen konvertieren.
Isaac Rabinovitch
3
Isaac, aufgrund des identifizierten Problems, das durch die Angabe :test_method_names_become_symbolsim Vergleich erzeugt wird, wird der Vergleich immer wahr sein. Durch Konvertieren all_symbolsin Zeichenfolgen und Vergleichen von Zeichenfolgen können wir unterscheiden, ob das Symbol vor dem Vergleich vorhanden war.
Stephen
3
Soviel verstehe ich nicht. Wenn ich >> array_of_symbols = Symbol.all_symbols, dann >> array_of_symbols.include? (: Not_yet_used) mache, werde ich falsch und für etwas, das definiert wurde, wahr, sodass ich nicht verstehe, warum die Konvertierung in Zeichenfolgen erforderlich ist .
Codenoob
74

Denn wenn Sie dies tun:

assert_equal true, all_symbols.include?(:test_method_names_become_symbols)

Es kann (abhängig von Ihrer Ruby-Implementierung) automatisch wahr sein, da :test_method_names_become_symbolsdas Symbol erstellt wird. Siehe diesen Fehlerbericht .

Andrew Grimm
quelle
1
Die akzeptierte Antwort ist also die falsche (oder so scheint es mir).
Isaac Rabinovitch
3
Isaac, die andere Antwort ist nicht falsch, aber nicht so präzise erklärt. sicher. Wie auch immer, Sie können überprüfen, was Andrew sagt, indem Sie Folgendes bestätigen: assert_equal true, Symbol.all_symbols.include? (: Abcdef) Dies wird unabhängig vom Symbol immer bestanden (zumindest für mich). Ich denke, eine Lektion ist, versuchen Sie nicht, das Vorhandensein / Fehlen von Symbolen als boolesches Flag zu verwenden.
Stephen
1
Wenn ich mir diese Frage ansehe, die mich auch 2 Jahre später noch interessiert, schätze ich diese Antwort und die Kommentare sehr. Ich denke, Isaac hat Recht. Dies ist die Antwort, die am deutlichsten erklärt, warum die Konvertierung in Zeichenfolgen der richtige Weg sein kann, obwohl ich denke, Sie könnten den Zwischenschritt (alle Symbole vor dem Vergleich speichern) einsetzen, damit es besser funktioniert.
Codenoob
3

Beide Antworten oben sind richtig, aber angesichts der obigen Frage von Karthik dachte ich, ich würde einen Test veröffentlichen, der zeigt, wie man ein Symbol genau an die includeMethode übergeben kann

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

Ein zusätzlicher Hinweis zu den Koans: Verwenden Sie putsAnweisungen sowie benutzerdefinierte Tests, wenn Sie nichts verstehen. Zum Beispiel, wenn Sie sehen:

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

und keine Ahnung haben, was sein wordskönnte, fügen Sie die Zeile hinzu

puts words

und führen Sie rakedie Befehlszeile aus. Ebenso können Tests wie der oben hinzugefügte hilfreich sein, um einige der Nuancen von Ruby zu verstehen.

aceofbassgreg
quelle
Wenn ich mir diese Frage ansehe, die mich auch 2 Jahre später noch interessiert, schätze ich diese Antwort und die Kommentare sehr.
Codenoob