12

我的问题建立在这个问题之上: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)

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

4

3 回答 3

12

这是我得到的:

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
于 2013-12-13T09:11:20.090 回答
5

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>
于 2012-11-08T19:02:57.777 回答
5

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

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

assert_equal true, all_symbols.include?(:RubyConstant)

我认为这是对公案的预期答案,这就是为什么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直接进行这些调用,但不是解决这个公案所必需的。

于 2012-11-08T23:48:58.400 回答