【问题标题】:Ruby Koans #75 test_constants_become_symbols, correct answer?Ruby Koans #75 test_constants_become_symbols,正确答案?
【发布时间】:2012-10-29 01:07:45
【问题描述】:

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

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

【问题讨论】:

  • 只要你依赖include?只返回truefalse,而不是assert_equal,你可以只使用assert Symbol.all_symbols.include( "RubyConstant" )
  • 我认为对于这个练习,我被要求填写空白。但是,是的,assert Symbol.all_symbols.include("RubyConstant") 也应该返回 true。谢谢!

标签: ruby symbols


【解决方案1】:

这是我得到的:

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

【讨论】:

  • 这是 Koan 正在寻找的答案,因为 RubyConstant 将是在程序执行期间创建的 Symbol
【解决方案2】:

注意:以下答案仅适用于 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 进行这些调用,但对于解决此公案不是必需的。

【讨论】:

  • 嗯,听起来不错。你知道为什么assert_equal true, all_symbols.include?(:WhateverConstant) 还在我身边吗?
  • 嗯.. 我想这比我预期的要复杂。如果逐行执行代码,例如在IRB中,我说的是正确的。但是,如果执行一个文件,Ruby 会在它实际执行任何操作之前扫描整个文件中的符号,这意味着符号包含在 Symbol.all_symbols 中,即使它们是在您分配 @987654330 的行之后定义的@。我会在我的回答上做个注释以反映这一点。
【解决方案3】:

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>

【讨论】:

  • 你在这里说的是正确的,但是因为 Symbol.all_symbols 返回的是数组的副本而不是直接引用,如果你做如下的事情,你可以避免这个问题:syms = Symbol.all_symbols; syms.include?(:DoesNotMatter)
  • 原来我所说的只是在一个奇怪的边缘情况下是正确的,请参阅我的答案的更新以了解详细信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-11-13
  • 2016-06-20
  • 1970-01-01
  • 1970-01-01
  • 2016-08-17
  • 2021-12-30
  • 1970-01-01
相关资源
最近更新 更多