在Ruby Koans的帮助下尝试Ruby .那里有以下测试:
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?
Run Code Online (Sandbox Code Playgroud)
我尝试在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
Run Code Online (Sandbox Code Playgroud)
问题是:为什么我们在这种情况下将符号转换为字符串以及为什么在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)'
Run Code Online (Sandbox Code Playgroud)
当您尝试此操作时irb,将在下一行之前解析和评估每一行.当你点击第二行时,symbols将包含:test_method因为def test_method;end已经被评估过了.但是,:test_method_nonexistent当我们遇到第2行时,这个符号在任何地方都没有出现,因此第4行和第5行会说"假".当然,第6行会给我们另一个错误,因为:really_not_there直到eval返回之后才会存在.所以irb说:
true
false
false
false
Run Code Online (Sandbox Code Playgroud)
如果将其作为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)
Run Code Online (Sandbox Code Playgroud)
将打印"true".然后我们点击第6行:
eval 'puts symbols.include?(:really_not_there)'
Run Code Online (Sandbox Code Playgroud)
并且打印"false",因为它:really_not_there是eval在运行时而不是在解析期间创建的.结果是Ruby说:
true
true
true
false
Run Code Online (Sandbox Code Playgroud)
如果我们在最后添加:
symbols = Symbol.all_symbols
puts symbols.include?('really_not_there'.to_sym)
Run Code Online (Sandbox Code Playgroud)
然后我们将从两者irb和独立脚本中获得另一个"真实",因为它eval已经创建了:really_not_there,我们将抓取符号列表的新副本.