Ruby Koans:为什么要将符号列表转换为字符串

cod*_*ode 80 ruby

我在Ruby Koans的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?
Run Code Online (Sandbox Code Playgroud)

为什么我们必须先将该列表转换为字符串?

Abo*_*uby 105

这与符号的工作方式有关.对于每个符号,实际上只存在其中一个符号.在幕后,符号只是一个名称引用的数字(以冒号开头).因此,在比较两个符号的相等性时,您要比较对象标识而不是引用此符号的标识符的内容.

如果您要进行简单的测试:test =="test",那将是错误的.因此,如果要将到目前为止定义的所有符号收集到一个数组中,则需要先将它们转换为字符串,然后再进行比较.你不能以相反的方式做到这一点(首先将要比较的字符串转换为符号),因为这样做会创建该符号的单个实例,并使用您正在测试存在的符号"污染"您的列表.

希望有所帮助.这有点奇怪,因为您必须测试符号的存在而不会在测试期间意外创建该符号.你通常看不到那样的代码.

  • 这仍然存在创建无法销毁的符号的问题.该符号的任何未来测试都将被破坏.但这只是一个Koan,它不需要太多意义或快速,只是演示符号如何工作. (4认同)
  • Isaac,由于发现了问题,在比较中指定`:test_method_names_become_symbols`将创建它,因此比较将始终为真.通过将`all_symbols`转换为字符串并比较字符串,我们可以区分符号在比较之前是否存在. (3认同)
  • 我不明白.如果我做>> array_of_symbols = Symbol.all_symbols,那么>> array_of_symbols.include?(:not_yet_used),我得到了假,我得到了定义的东西,所以我不明白为什么转换为字符串是必要的. (3认同)
  • 请注意,更安全地执行此操作的方法是将`Symbol.all_symbols`的输出分配给变量,然后测试是否包含.相比之下,符号更快,并且您避免将数千个符号转换为字符串. (2认同)
  • 这个答案对我不起作用。如果我们正在寻找符号的存在,为什么我们要为include指定字符串参数呢? (2认同)
  • @codenoob:是的,你是对的,我们可以比较这样的符号.但是,如果再次运行Symbols.all_symbols,您将看到:not_yet_used正在创建.所以关键是如果我们试图直接检查符号而不使用临时变量,那么它将永远为真 (2认同)

And*_*imm 73

因为如果你这样做

assert_equal true, all_symbols.include?(:test_method_names_become_symbols)
Run Code Online (Sandbox Code Playgroud)

它可能(取决于你的ruby实现)自动成为真,因为询问:test_method_names_become_symbols创建它.请参阅此错误报告.

  • 艾萨克,另一个答案没错,但并没有简明扼要地解释.当然.无论如何,你可以通过以下方式验证安德鲁的说法:assert_equal true,Symbol.all_symbols.include?(:abcdef)无论符号如何,这总是会通过(至少对我而言).我想一个教训是,不要尝试使用符号的存在/不存在作为布尔标志. (3认同)

ace*_*reg 5

上面的两个答案都是正确的,但是根据 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
Run Code Online (Sandbox Code Playgroud)

关于公案的附加说明:puts如果您不理解任何内容,请使用陈述和自定义测试。例如,如果您看到:

string = "the:rain:in:spain"
words = string.split(/:/)
Run Code Online (Sandbox Code Playgroud)

并且不知道words可能是什么,添加行

puts words
Run Code Online (Sandbox Code Playgroud)

rake在命令行运行。同样,像我上面添加的测试这样的测试也有助于理解 Ruby 的一些细微差别。