为什么没有带<的子表达式?首先在我的布尔表达式中进行评估?

时间:2013-08-19 15:25:01

标签: ruby operator-precedence boolean-expression

我用于Ruby运算符的引用是http://phrogz.net/programmingruby/language.html#table_18.4根据此引用和我所见过的其他引用,相等运算符优先于逻辑AND(&&,而不是Ruby& #39; s 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 < 5应该首先发生,导致AND之前的错误甚至可以被评估。我错过了什么吗?

5 个答案:

答案 0 :(得分:3)

TL; DR

<的优先级高于&&,这会影响Ripper对您的表达式进行标记的方式。 标记后,进行评估。

布尔和S-Expressions

在您的示例中,foo && foo < 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) && (foo < 5),因为<的优先级高于&&。但是,由于Ruby对布尔运算使用短路评估,因此除非foo计算为true,否则它永远不会评估布尔表达式的右侧。

答案 1 :(得分:1)

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

我可以重写

foo && foo < 5

作为

foo && foo.<(5)

现在更有意义。您可以从以下语句中获得相同的行为:

foo && foo.even?

您希望首先评估foo,然后才foo.even?。在foo之前评估foo.<(5)或在foo < 5之前评估时,同样如此。

现在我试图想出一个例子,其中&&<根据优先级表确实行为,但仍然没有成功。

答案 2 :(得分:0)

&&||从左到右评估运营商。一旦知道陈述的真实性或虚假性,评估就会停止。永远不会评估foo < 5。这就是第二种方法有效的原因。

此外,&&||的优先级要高得多。它们与andor不同。

答案 3 :(得分:0)

优先权是关于约束,不一定是操作的顺序。如果&&的LHS评估为假,那么它根本不会对RHS进行评估。

Ruby旨在实现某种功能,所以我会给你一些Haskell来更详细地展示它:

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

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

答案 4 :(得分:0)

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

解析器当然会按照下面的描述执行操作。但评估发生在那一步之后。

使用parser gem来检查代码,我们可以得到一些关于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)))))

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