为啥“真或真与假”似乎同时是真假?

Posted

技术标签:

【中文标题】为啥“真或真与假”似乎同时是真假?【英文标题】:Why does "true or true and false" appear to be simultaneously true and false?为什么“真或真与假”似乎同时是真假? 【发布时间】:2019-01-30 05:07:37 【问题描述】:

我得到以下信息:

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(就像薛定谔的猫)?

【问题讨论】:

true or (true and false)(true or true) and false 我在 irb 中用“puts true or true and false”注意到了这一点。我得到了“真”,就在它下面,“=>假” Ruby precedence 我认为这是一个很好的问题,因为运算符优先级和词法绑定不是任何语言最直观的方面,而 Ruby 的表现力可以让它变得更加棘手。除非您知道需要查找它们,否则您无法查找它们,即便如此,我认为结果可能会出现令人惊讶的边缘情况。 See below 来自解析器的具体示例。 量子红宝石! ?????? 【参考方案1】:

它与优先级有关。 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 指南建议在布尔表达式中使用 &&|| 而不是 andor 的原因之一。 puts true || true && false 的计算结果为 puts (true || (true && false))if true || true && false 的计算结果为 if (true || (true && false)),两者都符合您的预期。

【讨论】:

这是的原因。 :) 请注意puts true || true && false 输出true 并在irb 中返回nil,而puts true or true and false 输出true 并返回false&& 优先于 ||,后者优先于 andor(具有相同的优先级) 您的第一句话有误,这是可以理解的,因为布尔语句非常模棱两可。根据 Ripper 返回的 the S-tree,Ruby 实际上将其评估为更接近 (puts(true) or true) and (false)。不过,通过更高优先级的运算符和括号来减少歧义通常是不错的建议。 @ToddA.Jacobs 啊,有趣。是的,这真是一场噩梦。 @Caroline Kernel#puts 从不返回 false;它总是返回 nil 或引发异常(例如,如果标准输出已关闭)。然而,Ruby 中几乎所有的东西都是一个表达式。真|| true && false 或 puts true 或 true 和 false 返回 false,因为这是解析器评估的最后一个表达式,而不是因为 puts 评估(或返回)true 或 false。【参考方案2】:

优先级

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到标准输出。

【讨论】:

【参考方案3】:

这里发生了两件事。

评估

if true or true and false
  puts "that's true!"
else
  puts "that's false!"
end

true or true and false 计算结果为假。这就是that's false! 输出的原因。

优先级

or 的优先级低于||。这可能会造成混乱的情况,因为设置的值与正在评估的值不同。例如:

a = false || true
=> true
puts a
true

a = false or true
=> true
puts a
false

在第二个示例中,evaluationtrue,但 precedence 将 a 设置为 false。我希望这会有所帮助,我发现 this 资源非常有帮助。

【讨论】:

但我们可以进一步扩展您的优秀示例。如果我们写 a = true 或 false,则 puts a 返回 true。如果我们写 a = true 和 false,则 puts a 返回 true。但是,在 a = false 和 true 中,放置 a 返回 false。这是为什么?如果我们使用 && 和 ||,无论真假在先。如果我们使用和/或,a 将成为第一个布尔值。 那是因为 &&|| 胜过 = 符号。 andor 排名低于 =。所以在最后一个例子中a = false and true 将首先设置a = false 我承认这里的其他答案更深入。但过于冗长的答案会使事情变得更加混乱。【参考方案4】:

这是我第二次尝试从大家对我的问题的热烈回应中总结我的理解。对工程师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

【讨论】:

足够接近:)。我唯一要注意的是关于这个puts false or true && true 这实际上看起来更像(puts(false)) or (true && true) 所以评估实际上是nil or (true && true) 然后nil or true 但我很高兴广泛的对话和答案让你学到了一些+1的坚韧

以上是关于为啥“真或真与假”似乎同时是真假?的主要内容,如果未能解决你的问题,请参考以下文章

小冰超级自然语音技术发布!PK真人真假难辨,同时获独角兽轮融资

为啥 StringBuilder 访问似乎是同步的? [复制]

Python进阶:对象复制与比较,分深浅,见真假

工程布尔比较等于真假,为啥?

JQuery - 为啥 JQuery 动画是同时的?

为啥Android警报会同时触发?