【问题标题】:Why doesn't a sub-expression with < get evaluated first in my Boolean expression?为什么在我的布尔表达式中不首先评估带有 < 的子表达式?
【发布时间】:2013-08-21 11:13:12
【问题描述】:

我用于 Ruby 运算符的参考是 http://phrogz.net/programmingruby/language.html#table_18.4 根据这个参考,以及我见过的其他参考,相等运算符优先于逻辑 AND(&amp;&amp;,而不是 Ruby 的 and)。

我有以下几点:

foo = nil
foo < 5 # NoMethodError: undefined method `<' for nil:NilClass

要检查foo,我们这样做:

foo && (foo < 5) # Note the parenthesis

但这有效:

foo && foo < 5 # why does this work?

由于运算符优先级,foo &lt; 5 应该首先发生,导致在 AND 甚至可以计算之前出现错误。我错过了什么吗?

【问题讨论】:

  • 简短回答:评估发生在标记化之后。下面有更详细的解释。

标签: ruby operator-precedence boolean-expression


【解决方案1】:

TL;DR

&lt; 的优先级高于&amp;&amp;,这会影响Ripper 如何标记您的表达式。评估发生在 标记化之后。

布尔值和 S 表达式

在您的示例中,foo &amp;&amp; foo &lt; 5 被标记为两个表达式:

require 'ripper'
Ripper.sexp 'foo && foo < 5'
#=> [:program,
# [[:binary,
#   [:vcall, [:@ident, "foo", [1, 0]]],
#   :"&&",
#   [:binary, [:vcall, [:@ident, "foo", [1, 7]]], :<, [:@int, "5", [1, 13]]]]]]

因此,解析器认为这在功能上等同于(foo) &amp;&amp; (foo &lt; 5),因为&lt; 的优先级高于&amp;&amp;。但是,由于 Ruby 对布尔运算使用短路求值,它永远不会求值布尔表达式的右侧,除非 foo 求值为 true

【讨论】:

  • 感谢您的精彩回答和整理我的问题!
【解决方案2】:

以下内容帮助我理解了这个问题:

我可以重写

foo && foo < 5

作为

foo && foo.<(5)

现在更有意义了。您会期望以下语句具有相同的行为:

foo && foo.even?

您会期望首先评估foo,然后才评估foo.even?。当foofoo.&lt;(5) 之前被评估时也是如此,或者你可以写它foo &lt; 5

现在我正在尝试提出一个示例,其中 &amp;&amp;&lt; 确实按照优先级表运行,但仍然没有成功。

【讨论】:

  • 谢谢,这有助于我更好地理解它。就像你说的和他们真正按照优先级表表现的例子会很棒!
【解决方案3】:

&amp;&amp;|| 从左到右计算运算符。一旦知道陈述的真假,评估就会停止。 foo &lt; 5 永远不会被评估。这就是第二种方法有效的原因。

另外,&amp;&amp;|| 具有更高的优先级。它们与andor 不同。

【讨论】:

  • 我仍然不完全相信...在我链接的参考文献中,&amp;&amp; 的优先级低于&lt;。因此,即使 &amp;&amp; 在第一个错误信号处停止,也应首先评估 &lt;
  • 也许这devblog.avdi.org/2010/08/02/using-and-and-or-in-ruby 有帮助。 && 和 || ruby 中的语言与其他语言略有不同。
  • 一般来说文章不错,但它解决了orand 的作用,这不是我们要讨论的。
  • 您使用的是哪个版本的 ruby​​?我刚刚在我的 comp 上尝试了 irb 中的代码:hrishi>irb irb(main):001:0> foo = nil => nil irb(main):002:0> foo <' for nil:NilClass from (irb):2 from /usr/bin/irb:12:in
    ' irb(main):003:0> foo && (foo nil irb(main):004:0> foo && foo nil irb(main):005:0>
  • 奇怪,我用的是 2.0.0p0
【解决方案4】:

优先级是关于绑定的,不一定是操作顺序。如果&amp;&amp; 的 LHS 评估为 false,那么它根本不会评估 RHS。

Ruby 旨在成为一种函数式,所以我会给你一些 Haskell 来更详细地展示这一点:

true && x = x
false && x = false

由于左边是假的,所以它永远不会继续计算右边,所以它会崩溃。这是一些非常简单的懒惰评估。在第二行,x 的值无关紧要,因此它从不需要计算它,因为它不需要。这就是懒惰评估的美妙之处。 :)

【讨论】:

    【解决方案5】:

    &amp;&amp; 短路,如果foonilfalse,则不会评估右侧。

    解析器当然会做如下所述的事情。但评估发生在该步骤之后。

    使用parsergem 来检查代码,我们可以得到一些关于Ruby 解析器在做什么的提示。

    我有这两个文件:

    foo1.rb

    foo = nil
    foo && foo < 5
    

    AST 表示:

    (begin
      (lvasgn :foo
        (nil))
      (and
        (lvar :foo)
        (send
          (lvar :foo) :<
          (int 5))))
    

    foo2.rb

    foo = nil
    foo && (foo < 5)
    

    AST 表示:

    (begin
      (lvasgn :foo
        (nil))
      (and
        (lvar :foo)
        (begin
          (send
            (lvar :foo) :<
            (int 5)))))
    

    我不知道这是否澄清或混淆。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-12-15
      • 2017-12-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-08
      • 1970-01-01
      相关资源
      最近更新 更多