0

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

4

5 回答 5

3

TL;博士

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

布尔值和 S 表达式

在您的示例中,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.

于 2013-08-19T16:38:15.637 回答
1

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

我可以重写

foo && foo < 5

作为

foo && foo.<(5)

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

foo && foo.even?

你会期望首先foo被评估,然后才被评估foo.even?。在此foo之前进行评估foo.<(5)或您可以编写它时也是如此foo < 5

现在我试图提出一个例子,在哪里&&<真正按照优先表行事,但仍然没有成功。

于 2013-08-19T15:52:40.833 回答
0

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

而且,&&并且||具有更高的优先级。它们与and和不同or

于 2013-08-19T15:31:18.787 回答
0

&&短路,如果是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)))))

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

于 2013-08-19T16:39:03.423 回答
0

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

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

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

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

于 2013-08-19T15:31:58.487 回答