为啥“真或真与假”似乎同时是真假?
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 指南建议在布尔表达式中使用 &&
和 ||
而不是 and
和 or
的原因之一。 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
。 &&
优先于 ||
,后者优先于 and
和 or
(具有相同的优先级)
您的第一句话有误,这是可以理解的,因为布尔语句非常模棱两可。根据 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
在第二个示例中,evaluation 是 true
,但 precedence 将 a 设置为 false
。我希望这会有所帮助,我发现 this 资源非常有帮助。
【讨论】:
但我们可以进一步扩展您的优秀示例。如果我们写 a = true 或 false,则 puts a 返回 true。如果我们写 a = true 和 false,则 puts a 返回 true。但是,在 a = false 和 true 中,放置 a 返回 false。这是为什么?如果我们使用 && 和 ||,无论真假在先。如果我们使用和/或,a 将成为第一个布尔值。 那是因为&&
和 ||
胜过 =
符号。 and
和 or
排名低于 =
。所以在最后一个例子中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真人真假难辨,同时获独角兽轮融资