Ruby Koans#75 test_constants_become_symbols,正确答案?

时间:2012-11-08 18:52:56

标签: ruby symbols

我的问题建立在这个问题上:Ruby Koan: Constants become symbols。我有以下代码:

in_ruby_version("mri") do
  RubyConstant = "What is the sound of one hand clapping?"
  def test_constants_become_symbols
    all_symbols = Symbol.all_symbols

    assert_equal __, all_symbols.include?(__)
  end
end

正确的答案应该如下吗?

    assert_equal true, all_symbols.include?("RubyConstant".to_sym)

我知道我不应该这样做:

    assert_equal true, all_symbols.include?(:RubyConstant)

因为那时我可以放任何东西而且它仍然是真的

    assert_equal true, all_symbols.include?(:DoesNotMatter)

提前道歉,问一个简单的“是或否”问题。我很好奇知道“正确”的答案是什么。我本来希望在我上面提到的上一篇文章的评论中提出这个问题,但我不能不单独发帖。

3 个答案:

答案 0 :(得分:9)

这是我得到的:

in_ruby_version("mri") do
  RubyConstant = "What is the sound of one hand clapping?"
  def test_constants_become_symbols
    all_symbols_as_strings = Symbol.all_symbols.map { |x| x.to_s }

    assert_equal true, all_symbols_as_strings.include?("RubyConstant")
  end
end

答案 1 :(得分:5)

注意:以下答案仅适用于像irb这样的环境,其中Ruby代码是逐行执行的。在文件中执行代码时,Ruby会在执行任何操作之前扫描整个文件中的符号,因此以下详细信息不准确。我没有删除这个答案,因为它暴露了一个有趣的边缘情况,但请参阅@GlichMr的答案以更好地解释问题。

您可以安全地执行以下操作,因为Symbol.all_symbols会返回符号数组的副本,而不是引用。

assert_equal true, all_symbols.include?(:RubyConstant)

我认为这是koan的预期答案,这就是定义all_symbols而不是直接调用Symbol.all_symbols的原因。有关证据,请参阅以下内容:

>> X = 1
=> 1
>> all_symbols = Symbol.all_symbols; nil
=> nil
>> Y = 2
=> 2
>> all_symbols.include?(:X)
=> true
>> all_symbols.include?(:Y)
=> false

使用String#to_sym可以直接针对Symbol.all_symbols进行这些调用,但不是解决此问题所必需的。

答案 2 :(得分:4)

Symbol.all_symbols包含引用的每个符号 - 变量名,类名,常量名,实际符号。该变量实际包含的是实现定义,但在Ruby MRI中,此列表中已有许多符号。

irb(main):001:0> Constant = 42
=> 42
irb(main):002:0> Symbol.all_symbols
=> [:"", :"<IFUNC>", :"<CFUNC>", :respond_to?,  ..., :irb_exit_org, :Constant]

但现在,有一个问题。

Symbol.all_symbols.include?(:DoesNotMatter)

在运行此代码之前,:DoesNotMatter中不存在all_symbols,但它仍然存在。好吧,实际上当你使用符号文字时,它被插入Symbol.all_symbols(除非它已经存在)。因此,在您调用.include?之前,符号已经存在。

编辑:Gregory Brown建议采取以下措施。它的工作原理是因为在Ruby中为Symbol.all_symbols复制变量由于某种原因而不是复制对变量的引用。

irb(main):001:0> symbols = Symbol.all_symbols; 1
=> 1
irb(main):002:0> symbols.include? :something
=> false
irb(main):003:0>