优先级
Ruby 的解析器依赖于precedence table。在您的示例中,or 的优先级高于and。此外,如果第一个表达式为真,短路评估将不会评估 or 条件的第二项。
另外,请注意,在 Ruby 中,Kernel#puts 是一个接受可选参数的方法,而 if 是一个词法标记。虽然许多人在惯用的 Ruby 中省略了括号,但优先级可以改变解析器在评估复杂或模棱两可的表达式(如 true or true and false)时看到的内容。正如您将在下面看到的,条件和方法之间的区别使事情变得更加复杂。
一般来说,应为表达式加上括号以避免歧义,并尽可能少地依赖运算符优先级。总是有例外,尤其是在像 Ruby 这样的表达性语言中,但根据经验,它可以极大地简化 Rubyist 的生活。
检查解析器
如果您对解析器看到的内容有疑问,您不必仅依靠推理。您可以使用Ripper module 检查符号表达式树。例如:
require 'pp'
require 'ripper'
pp Ripper.sexp 'true or true and false'
这会告诉你:
[:program,
[[:binary,
[:binary,
[:var_ref, [:@kw, "true", [1, 0]]],
:or,
[:var_ref, [:@kw, "true", [1, 8]]]],
:and,
[:var_ref, [:@kw, "false", [1, 17]]]]]]
这表明解析器认为表达式本身的计算结果就像您将括号括起来为 (true or true) and false。
同样,if 语句具有相同的优先级:
pp Ripper.sexp 'if true or true and false; end'
[:program,
[[:if,
[:binary,
[:binary,
[:var_ref, [:@kw, "true", [1, 3]]],
:or,
[:var_ref, [:@kw, "true", [1, 11]]]],
:and,
[:var_ref, [:@kw, "false", [1, 20]]]],
[[:void_stmt]],
nil]]]
但是,因为puts是一个方法,所以解析不同:
pp Ripper.sexp 'puts true or true and false'
[:program,
[[:binary,
[:binary,
[:command,
[:@ident, "puts", [1, 0]],
[:args_add_block, [[:var_ref, [:@kw, "true", [1, 5]]]], false]],
:or,
[:var_ref, [:@kw, "true", [1, 13]]]],
:and,
[:var_ref, [:@kw, "false", [1, 22]]]]]]
换句话说,解析器假定您的模棱两可的语句大致等同于以下带括号的表达式:(puts(true) or true) and (false)。在这种情况下,第一个 true 被假定为 Kernel#puts 的参数。由于 puts 方法总是返回 nil(这是错误的),因此第二个 true 被评估,使 puts(true) or true 为真。接下来,终端表达式被求值并返回false,不管puts语句打印 true到标准输出。