使用include检查符号是否存在于数组中?
在Ruby Koans的帮助下尝试Ruby。有下面的测试有:使用include检查符号是否存在于数组中?
def test_method_names_become_symbols
symbols_as_strings = Symbol.all_symbols.map { |x| x.to_s }
assert_equal __, 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?
我试图做同样的事情在IRB控制台,它为未定义的方法返回false
。但之后我在test.rb
文件中尝试了相同的操作,并且true
已返回到现有和未知方法。
示例代码:
def test_method
end
symbols = Symbol.all_symbols.map { |x| x }
puts symbols.include?(:test_method) # returns true in both cases
puts symbols.include?(:test_method_nonexistant) # returns false in irb, true if executed directly
的问题是:为什么我们的符号转换为字符串,在这种情况下,为什么有在IRB和正常的文件不同的结果?
谢谢!
让我们通过你的测试代码稍加修改的版本,因为它是由irb
并作为一个独立的脚本看出:
def test_method;end
symbols = Symbol.all_symbols # This is already a "fixed" array, no need for map
puts symbols.include?(:test_method)
puts symbols.include?('test_method_nonexistent'.to_sym)
puts symbols.include?(:test_method_nonexistent)
eval 'puts symbols.include?(:really_not_there)'
当您尝试这irb
,每一行都会被分析和评估在下一行之前。当你点击第二行时,symbols
将包含:test_method
,因为def test_method;end
已经被评估。但是,当我们击中第2行时,:test_method_nonexistent
符号在任何地方都没有出现过,所以第4行和第5行会说“错误”。第6行当然会给我们另一个错误,因为:really_not_there
在eval
返回后才会存在。所以irb
这样说:
true
false
false
false
如果你运行这个作为一个Ruby脚本,事情发生在略微不同的顺序。第一个Ruby将脚本解析为Ruby VM可以理解的内部格式,然后返回到第一行并开始执行脚本。在解析脚本时,解析第一行后将存在:test_method
符号,解析第五行后将存在:test_method_nonexistent
;所以,在脚本运行之前,我们感兴趣的两个符号是已知的。当我们击中第六行时,Ruby只会看到一个eval
和一个字符串,但它还不知道eval
会导致一个符号出现。
现在,我们有我们的两个符号(:test_method
和:test_method_nonexistent
)和一个简单的字符串,送入eval
时,将创建一个符号(:really_not_there
)的。然后我们回到开始,VM开始运行代码。当我们运行第2行并缓存我们的符号数组时,:test_method
和:test_method_nonexistent
都将存在并出现在symbols
数组中,因为解析器创建了它们。所以第3行到第5行:
puts symbols.include?(:test_method)
puts symbols.include?('test_method_nonexistent'.to_sym)
puts symbols.include?(:test_method_nonexistent)
将打印“true”。然后我们打线6:因为:really_not_there
由eval
在运行时,而不是在分析过程中产生
eval 'puts symbols.include?(:really_not_there)'
和“假”被打印出来。其结果是,红宝石说:
symbols = Symbol.all_symbols
puts symbols.include?('really_not_there'.to_sym)
然后,我们会得到另一个“真”走出两个irb
和独立脚本,因为eval
:
true
true
true
false
如果我们在末尾添加这将创建:really_not_there
,我们将抓取符号列表的新副本。
非常棒,非常感谢您的详细解释! –
对常规口译人员的不同行为进行细致分析。 +1 – coreyward
当检查符号的存在时,必须将符号转换为字符串的原因是否则它将始终返回true。传递给include?
方法的参数首先被评估,所以如果你传递一个符号,一个新的符号被实例化并添加到堆中,所以Symbol.all_symbols
实际上具有该符号的副本。
Symbol.all_symbols.include? :the_crow_flies_at_midnight #=> true
然而,转换数千符号为字符串进行比较(这是与符号快得多)是不良的溶液。更好的方法是改变顺序,这些声明得到评估:
symbols = Symbol.all_symbols
symbols.include? :the_crow_flies_at_midnight #=> false
这个“快照”的是什么符号在字典中我们测试的符号插入到堆上前服用,所以尽管我们的观点在调用include?
方法时存在于堆上,我们正在获得我们期望的结果。
我不知道它为什么不能在IRB控制台中工作。也许你打错了。
谢谢你,这很有道理。但为什么这个新符号会出现在我的'symbols'数组中。它在某个时候形成,为什么它会改变?另外,我确定我没有错误输入,你可以自己尝试。 –
[Ruby Koans:为什么要将符号列表转换为字符串](http://stackoverflow.com/questions/4686097/ruby-koans-why-convert-list-of-symbols-to-strings) –