我得到以下信息:
puts true or true and false
# >> true
我还能得到:
if true or true and false
puts "that's true!"
else
puts "that's false!"
end
# >> that's false!
为什么true or true and false
和true
都false
(如薛定ding的猫)?
答案 0 :(得分:7)
它与优先级有关。 puts true or true and false
实际上评估为(puts true) or (true and false)
[编辑:不完全是。请参见下面来自Todd的注释。] ,并且if true or true and false
的评估结果为if (true or (true and false))
。这是由于puts
(一种方法)和if
(一种语言关键字)相对于表达式的其他术语的优先级。
当评估=> false
时,您会在irb中看到puts true or true and false
(请记住,就是(puts true) or (true and false)
),因为puts
输出true
并返回nil
,这是错误的,导致(true and false)
接下来被评估,并返回false
。
这是大多数Ruby指南建议在布尔表达式中使用&&
和||
而不是and
和or
的原因之一。 puts true || true && false
的评估为puts (true || (true && false))
,而if true || true && false
的评估为if (true || (true && false))
,两者都符合您的期望。
答案 1 :(得分:3)
Ruby的解析器依赖于precedence table。在您的示例中,or
的优先级高于and
。此外,如果第一个表达式为真,则短路评估将不会评估或条件的第二项。
此外,请注意,在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
到标准输出的事实如何,都要对终端表达式求值并返回true
。
答案 2 :(得分:2)
这里有两件事。
评估
if true or true and false
puts "that's true!"
else
puts "that's false!"
end
true or true and false
的计算结果为false。这就是that's false!
输出的原因。
优先级
or
的优先级低于||
。这可能会造成混乱,因为设置的值与所评估的值不同。例如:
a = false || true
=> true
puts a
true
a = false or true
=> true
puts a
false
在第二个示例中,评估为true
,但优先级将a设置为false
。希望对您有所帮助,我发现this资源非常有帮助。
答案 3 :(得分:1)
这是我的第二次尝试,从每个人对我的问题的强烈回应中总结出我的理解。特别感谢工程师mnky和Joseph Cho。阅读几次答案后,灯泡点亮。
puts false or true && true
输出false
并返回true
puts false || true and false
输出true
并返回nil
在这种情况下,优先顺序为
和,或
puts false or true && true
变为puts false or true
。第一部分puts false
输出false
并返回nil
。语句现在为nil or true
,它返回true
类似地,puts false || true and false
变成puts true and false
。然后,第一部分puts true
输出true
并返回nil
。语句现在为nil and false
,它将返回nil
。