如何解释这个运算符结合性?
How to explain this operator associativity?
根据this table,++
具有从右到左的结合性。所以,我 运行 这个代码:
int a = 5;
++a + ++a * ++a
并期望表达式为 50
(因为 8 + 7 * 6
,增量从右到左开始)。但是 Eclipse 会从左到右 (6 + 7 * 8
) 计算表达式,并给出结果 62
。我是 Java 中的这种关联性的新手,肯定遗漏了一些明显的东西。请帮助我理解这种奇怪的行为。
编辑: 感谢您的回答,但我现在还有一个问题。就是说,从@bizclop 的代码和树答案可以看出,++
的关联性显然无关紧要。那么,对于++
/--
的这种关联性,是否有任何用例?
这是因为乘法运算符*
比加法运算符+
具有更高的优先级。
计算结果为++a + (++a * ++a) = 6 + 7 * 8
这里混淆了两种不同的东西:表达式解析和表达式求值。
让我们从表达式开始:++a + ++a * ++a
。我们首先与它有什么关系?由于运算符 +
和 *
需要两个操作数,而 ++
需要一个,我们必须弄清楚哪个操作数对应哪个操作。这是表达式解析步骤,其中应用 precedence/associativity。
++
具有最高的优先级,因此我们可以将表达式重写为 (++a) + (++a) * (++a)
- 接下来是
*
,所以我们可以再次添加括号:(++a) + ((++a) * (++a))
- 最后我们有
+
,所有优先级最低的,所以为了对称我们可以写成:((++a) + ((++a) * (++a)))
请注意,我们可以将其巧妙地表示为一棵树:
+
/ \
++ *
| | \
a ++ ++
| |
a a
这是我们基于优先级和结合性的评估树。
Note that in this case associativity didn't matter at all as all the operators
had different precedence. Had our expression been 4 - 1 - 3 + 2
, it
would've been important to use associativity to reach (((4 - 1) - 3) + 2)
, as +
and -
have the same precedence.
现在进入下一步,评估。求值 总是 从左到右发生(即使是赋值,虽然规则有点古怪),但是使用我们刚刚构建的树它是从左到右发生的。
- 因此,首先我们开始评估
+
运算符,因为它位于顶部。
- 然后我们向左下一层,当我们计算
+
运算符左侧的 ++a
时,(6)
- 然后我们往右走,开始评估
*
操作。
- 我们这样做的方法是评估第二个
++a
,(7)
- 然后第三个
++a
(8)
- 然后我们将步骤 4 和 5 的结果相乘(这是评估的
*
操作),(56)
- 最后我们将第 6 步的结果与第 2 步的结果相加 (62)
...我们完成了。
TL;DR:优先级和结合性决定了放置括号的位置,但求值总是从左到右。
The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right.
根据this table,++
具有从右到左的结合性。所以,我 运行 这个代码:
int a = 5;
++a + ++a * ++a
并期望表达式为 50
(因为 8 + 7 * 6
,增量从右到左开始)。但是 Eclipse 会从左到右 (6 + 7 * 8
) 计算表达式,并给出结果 62
。我是 Java 中的这种关联性的新手,肯定遗漏了一些明显的东西。请帮助我理解这种奇怪的行为。
编辑: 感谢您的回答,但我现在还有一个问题。就是说,从@bizclop 的代码和树答案可以看出,++
的关联性显然无关紧要。那么,对于++
/--
的这种关联性,是否有任何用例?
这是因为乘法运算符*
比加法运算符+
具有更高的优先级。
计算结果为++a + (++a * ++a) = 6 + 7 * 8
这里混淆了两种不同的东西:表达式解析和表达式求值。
让我们从表达式开始:++a + ++a * ++a
。我们首先与它有什么关系?由于运算符 +
和 *
需要两个操作数,而 ++
需要一个,我们必须弄清楚哪个操作数对应哪个操作。这是表达式解析步骤,其中应用 precedence/associativity。
++
具有最高的优先级,因此我们可以将表达式重写为(++a) + (++a) * (++a)
- 接下来是
*
,所以我们可以再次添加括号:(++a) + ((++a) * (++a))
- 最后我们有
+
,所有优先级最低的,所以为了对称我们可以写成:((++a) + ((++a) * (++a)))
请注意,我们可以将其巧妙地表示为一棵树:
+
/ \
++ *
| | \
a ++ ++
| |
a a
这是我们基于优先级和结合性的评估树。
Note that in this case associativity didn't matter at all as all the operators had different precedence. Had our expression been
4 - 1 - 3 + 2
, it would've been important to use associativity to reach(((4 - 1) - 3) + 2)
, as+
and-
have the same precedence.
现在进入下一步,评估。求值 总是 从左到右发生(即使是赋值,虽然规则有点古怪),但是使用我们刚刚构建的树它是从左到右发生的。
- 因此,首先我们开始评估
+
运算符,因为它位于顶部。 - 然后我们向左下一层,当我们计算
+
运算符左侧的++a
时,(6) - 然后我们往右走,开始评估
*
操作。 - 我们这样做的方法是评估第二个
++a
,(7) - 然后第三个
++a
(8) - 然后我们将步骤 4 和 5 的结果相乘(这是评估的
*
操作),(56) - 最后我们将第 6 步的结果与第 2 步的结果相加 (62)
...我们完成了。
TL;DR:优先级和结合性决定了放置括号的位置,但求值总是从左到右。
The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right.