为什么“真实或真实和错误”似乎同时是真实和错误?

时间:2018-08-23 18:35:55

标签: ruby operator-precedence conditional-operator

我得到以下信息:

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 falsetruefalse(如薛定ding的猫)?

4 个答案:

答案 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指南建议在布尔表达式中使用&&||而不是andor的原因之一。 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

在这种情况下,优先顺序为

  1. &&
  2. ||
  3. 投入
  4. 和,或

    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