Ruby Koans:記号のリストを文字列に変換する理由


85

RubyKoansのabout_symbols.rbでこのテストを参照してい ます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?

なぜ最初にそのリストを文字列に変換する必要があるのですか?

回答:


110

これは、シンボルがどのように機能するかと関係があります。シンボルごとに、実際に存在するのは1つだけです。舞台裏では、シンボルは名前で参照される単なる数字です(コロンで始まります)。したがって、2つのシンボルの同等性を比較する場合、このシンボルを参照する識別子の内容ではなく、オブジェクトのIDを比較します。

単純なテスト:test == "test"を実行すると、falseになります。したがって、これまでに定義されたすべての記号を配列にまとめる場合は、比較する前に、まずそれらを文字列に変換する必要があります。これを逆の方法で行うことはできません(最初に比較する文字列をシンボルに変換します)。これを行うと、そのシンボルの単一インスタンスが作成され、存在をテストしているシンボルでリストが「汚染」されます。

お役に立てば幸いです。テスト中に誤ってシンボルを作成せずにシンボルの存在をテストする必要があるため、これは少し奇妙なことです。通常、そのようなコードは表示されません。


2
これを安全に行うためのより良い方法は、の出力をSymbol.all_symbols変数に割り当ててから、包含をテストすることです。記号は比較すると高速であり、何千もの記号を文字列に変換することを回避できます。
— coreyward 2011年

4
それでも、破壊できないシンボルを作成するという問題があります。そのシンボルの将来のテストはすべて台無しになります。しかし、これは単なる公案であり、あまり意味がなく、高速である必要はなく、シンボルがどのように機能するかを示すだけです。
— AboutRuby 2011年

2
この答えは私にはうまくいきません。記号の存在を検索している場合、なぜ文字列引数を指定するのですか。指定したinclude?場合:test_method_names_become_symbols、これらの記号をすべて文字列に変換する必要はありません。
— Isaac Rabinovitch 2012

3
アイザック、特定された問題:test_method_names_become_symbols、つまり比較で指定すると作成されるため、比較は常に真になります。all_symbols文字列に変換して文字列を比較することにより、比較前に記号が存在していたかどうかを区別できます。
— スティーブン

3
よくわかりません。>> array_of_symbols = Symbol.all_symbols、次に>> array_of_symbols.include?(:not_yet_used)を実行すると、falseになり、定義されたものに対してtrueになるので、文字列への変換が必要な理由がわかりません。
— codenoob 2014

75

あなたがそうするなら:

assert_equal true, all_symbols.include?(:test_method_names_become_symbols)

:test_method_names_become_symbolsシンボルを作成するため、(rubyの実装によっては)自動的にtrueになる場合があります。このバグレポートを参照してください。


1
したがって、受け入れられた答えは間違ったものです(またはそう私には思えます)。
— Isaac Rabinovitch 2012

3
アイザック、他の答えは間違っていませんが、それは簡潔に説明されていません。確かに。とにかく、Andrewの言っていることを次のように確認できます:assert_equal true、Symbol.all_symbols.include?(:abcdef)これは、シンボルに関係なく常に合格します(少なくとも私にとっては合格です)。一つの教訓は、シンボルの有無をブールフラグとして使用しようとしないことだと思います。
— スティーブン

1
2年経った今でも興味を持っているこの質問を見て、この回答とコメントに心から感謝しています。Isaacは正しいと思います。これは、文字列への変換が進むべき道である理由を最も明確に説明する答えですが、それをより良く機能させるために中間ステップ(比較の前にすべての記号を格納する)を入れることができると思います。
— codenoob 2015年

3

上記の両方の答えは正しいですが、上記のKarthikの質問に照らして、includeメソッドにシンボルを正確に渡す方法を示すテストを投稿すると思いました。

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

Koansに関する追加の注意:puts何も理解していない場合は、ステートメントとカスタムテストを利用してください。たとえば、次の場合に表示されます。

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

何であるかわからwordsない場合は、行を追加します

puts words

rakeコマンドラインで実行します。同様に、上記で追加したようなテストは、Rubyの微妙な違いを理解する上で役立ちます。


2年経った今でも興味を持っているこの質問を見て、この回答とコメントに心から感謝しています。
— codenoob 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.