为什么“真实或真实与错误”似乎同时存在真假?
我得到以下内容:
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
都是false
(如薛定谔的猫)?
它与优先权有关。 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
(语言关键字)相对于表达式的其他项的优先级。
当你评估puts true or true and false
时,你会在irb中看到=> 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
计算为puts (true || (true && false))
, if true || true && false
if true || true && false
计算结果如同if (true || (true && false))
,正如您所期望的那样。
优先权
Ruby的解析器依赖于优先级表 。 在您的示例中, or
具有比and
更高的优先级。 另外,如果第一个表达式是真实的,则短路评估不会评估条件的第二项。
另请注意,在Ruby 内核中,puts是一个采用可选参数的方法,而if
是一个词法标记。 虽然许多人在惯用Ruby中省略了括号,但优先级可以改变解析器在评估复杂或模糊表达式(如true or true and false
时所看到的内容。 正如您将在下面看到的,条件和方法之间的区别使问题进一步复杂化。
通常,应该将表达式括起来以避免歧义,并尽可能少地依赖运算符优先级。 总有例外,尤其是像Ruby这样的富有表现力的语言,但作为经验法则,它可以极大地简化Rubyist的生活。
检查解析器
如果您对解析器看到的内容一直存在疑问,则不必单独依赖推理。 您可以使用Ripper模块检查符号表达式树。 例如:
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
,使得put puts(true) or true
truthy。 接下来,计算终端表达式并返回false
,而不管puts-statement是否将true
输出到标准输出。
这里有两件事。
评估
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
。 我希望这有帮助,我发现这个资源非常有用。
这是我第二次尝试总结我的理解,每个人都对我的问题做出了很好的回应。 对工程师和Joseph Cho的特别呐喊。 几次读完你的答案后,灯泡继续亮着。
puts false or true && true
输出false
并返回true
puts false || true and false
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 false || true and false
变得puts true and false
。 然后第一部分puts true
输出设为true
并返回nil
。 这些陈述现在nil and false
,将返回nil
。