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


85

我在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?

为什么我们必须首先将那个列表转换成字符串呢?

Answers:


110

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

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

希望能有所帮助。这有点奇怪,因为您必须测试符号的存在,而不能在测试过程中意外创建该符号。通常您不会看到这样的代码。


2
请注意,安全地执行此操作的更好方法是将输出分配给Symbol.all_symbols变量,然后测试是否包含。符号在比较时速度更快,并且避免了将数千个符号转换为字符串。
coreyward

4
仍然存在创建无法销毁符号的问题。该符号的任何将来测试都将被破坏。但这只是一个Koan,不必太有意义或太快,只需演示符号的工作原理即可。
关于Ruby

2
这个答案对我不起作用。如果我们要搜索符号的存在,为什么要为指定字符串参数?include?如果指定了,:test_method_names_become_symbols我们就不必将所有这些符号都转换为字符串。
艾萨克·拉比诺维奇

3
艾萨克(Isaac),由于已发现问题,即:test_method_names_become_symbols在比较中进行指定会创建该问题,因此比较将始终为真。通过转换all_symbols为字符串并比较字符串,我们可以在比较之前区分符号是否存在。
斯蒂芬

3
太多我不明白。如果我>> >> array_of_symbols = Symbol.all_symbols,那么>> array_of_symbols.include?(:not_yet_used),我得到的是假,对于定义的东西我是正确的,所以我不明白为什么必须转换为字符串。
codenoob 2014年

75

因为如果这样做:

assert_equal true, all_symbols.include?(:test_method_names_become_symbols)

由于:test_method_names_become_symbols创建了符号,它可能(取决于您的ruby实现)自动为true 。请参阅此错误报告


1
因此,可接受的答案是错误的答案(或者在我看来是这样)。
艾萨克·拉比诺维奇

3
以撒,另一个答案没有错,但没有给出简洁的解释。当然。无论如何,您可以使用以下命令验证Andrew的说法:assert_equal true,Symbol.all_symbols.include?(:abcdef)无论符号如何,这将始终(至少对我而言)通过。我想一个教训是,不要尝试使用符号的存在/不存在作为布尔标志。
斯蒂芬

1
看着这个仍然让我感兴趣的问题,两年后,我真的很感谢这个答案和评论。我认为Isaac是正确的,这是最清楚地解释了为什么转换为字符串的方法的答案,尽管我认为您可以采取中间步骤(将所有符号存储在比较器之前)以使其更好地工作。
codenoob

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的某些细微差别很有帮助。


看着这个仍然让我感兴趣的问题,两年后,我真的很感谢这个答案和评论。
codenoob
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.