加法赋值+ =表达式中的行为 [英] Addition assignment += behavior in expression

查看:119
本文介绍了加法赋值+ =表达式中的行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近我遇到了一个问题:对赋值运算符链的理解.

Recently I came across this question: Assignment operator chain understanding.

在回答这个问题时,我开始怀疑自己对加法赋值运算符+=或任何其他operator=(&=*=/=等)的行为的了解.

While answering this question I started doubting my own understanding of the behavior of the addition assignment operator += or any other operator= (&=, *=, /=, etc.).

我的问题是,下面的表达式中的变量a何时更新到位,以使其在评估过程中更改后的值反映在表达式的其他位置,其背后的逻辑是什么?请查看以下两个表达式:

My question is, when is the variable a in the expressions below updated in place, so that its changed value is reflected in other places in the expression during evaluation, and what is the logic behind it? Please take a look at following two expressions:

表达式1

a = 1
b = (a += (a += a))
//b = 3 is the result, but if a were updated in place then it should've been 4

表达式2

a = 1
b = (a += a) + (a += a)
//b = 6 is the result, but if a is not updated in place then it should've been 4

在第一个表达式中,当计算最里面的表达式(a += a)时,它似乎没有更新a的值,因此结果显示为3而不是4.

In the first expression, when the innermost expression (a += a) is evaluated, it seems that it doesn't update value of a, thus the result comes out as 3 instead of 4.

但是,在第二个表达式中,a的值被更新,因此结果为6.

However, in the second expression, the value of a is updated and so the result is 6.

我们什么时候应该假定a的值将反映在表达式中的其他位置,什么时候不应该?

When should we assume that a's value will be reflected in other places in the expression and when should we not?

推荐答案

请记住,a += x确实是a = a + x的意思.要理解的关键点是,加法从左到右进行评估-也就是说,a + x中的ax之前进行评估.

Remember that a += x really means a = a + x. The key point to understand is that addition is evaluated from left to right -- that is, the a in a + x is evaluated before x.

所以让我们弄清楚b = (a += (a += a))的作用.首先,我们使用规则a += x表示a = a + x,然后开始以正确的顺序仔细评估表达式:

So let's figure out what b = (a += (a += a)) does. First we use the rule a += x means a = a + x, and then we start evaluating the expression carefully in the correct order:

  • b = (a = a + (a = a + a)),因为a += x表示a = a + x
  • b = (a = 1 + (a = a + a)),因为a当前为1.请记住,我们先评估左侧术语a,再评估右侧术语(a = a + a)
  • b = (a = 1 + (a = 1 + a)),因为a仍然是1
  • b = (a = 1 + (a = 1 + 1)),因为a仍然是1
  • b = (a = 1 + (a = 2)),因为1 + 12
  • b = (a = 1 + 2),因为a现在是2
  • b = (a = 3),因为1 + 23
  • b = 3,因为a现在是3
  • b = (a = a + (a = a + a)) because a += x means a = a + x
  • b = (a = 1 + (a = a + a)) because a is currently 1. Remember we evaluate the left term a before the right term (a = a + a)
  • b = (a = 1 + (a = 1 + a)) because a is still 1
  • b = (a = 1 + (a = 1 + 1)) because a is still 1
  • b = (a = 1 + (a = 2)) because 1 + 1 is 2
  • b = (a = 1 + 2) because a is now 2
  • b = (a = 3) because 1 + 2 is 3
  • b = 3 because a is now 3

这使我们有了如上所述的a = 3b = 3.

This leaves us with a = 3 and b = 3 as reasoned above.

让我们尝试使用其他表达式b = (a += a) + (a += a):

Let's try this with the other expression, b = (a += a) + (a += a):

  • b = (a = a + a) + (a = a + a)
  • b = (a = 1 + 1) + (a = a + a),请记住我们先评估左项,再评估右项
  • b = (a = 2) + (a = a + a)
  • b = 2 + (a = a + a)a现在是2.开始评估正确的术语
  • b = 2 + (a = 2 + 2)
  • b = 2 + (a = 4)
  • b = 2 + 4a现在为4
  • b = 6
  • b = (a = a + a) + (a = a + a)
  • b = (a = 1 + 1) + (a = a + a), remember we evaluate the left term before the right one
  • b = (a = 2) + (a = a + a)
  • b = 2 + (a = a + a) and a is now 2. Start evaluating the right term
  • b = 2 + (a = 2 + 2)
  • b = 2 + (a = 4)
  • b = 2 + 4 and a is now 4
  • b = 6

这留下a = 4b = 6.可以通过在Java/JavaScript中打印出ab来验证这一点(两者在此具有相同的行为).

This leaves us with a = 4 and b = 6. This can be verified by printing out both a and b in Java/JavaScript (both have the same behavior here).

将这些表达式视为解析树也可能会有所帮助.当我们评估a + (b + c)时,将在RHS (b + c)之前评估LHS a.这是在树结构中编码的:

It might also help to think of these expressions as parse trees. When we evaluate a + (b + c), the LHS a is evaluated before the RHS (b + c). This is encoded in the tree structure:

   +
  / \
 a   +
    / \
   b   c

请注意,我们不再有任何括号-操作顺序已编码到树结构中.当我们评估树中的节点时,我们以固定顺序(即,+从左到右)处理节点的子级.例如,当我们处理根节点+时,我们在右子树(b + c)之前评估左子树a,而不管右子树是否用括号括起来(因为甚至不存在括号)在分析树中).

Note that we don't have any parentheses anymore -- the order of operations is encoded into the tree structure. When we evaluate the nodes in the tree, we process the node's children in a fixed order (i.e., left-to-right for +). For instance, when we process the root node +, we evaluate the left subtree a before the right subtree (b + c), regardless of whether the right subtree is enclosed in parentheses or not (since the parentheses aren't even present in the parse tree).

因此,与您可能会教过算术规则的相反,Java/JavaScript不会总是首先评估最嵌套的括号".

Because of this, Java/JavaScript do not always evaluate the "most nested parentheses" first, in contrast to rules you might have been taught for arithmetic.

请参见 Java语言规范:

15.7.评估单

Java编程语言保证运算符的操作数似乎按照特定的求值顺序(即从左到右)进行求值.
...

15.7. Evaluation Order

The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right.
...

在评估右手操作数的任何部分之前,似乎已对二元运算符的左手操作数进行了完全评估.

The left-hand operand of a binary operator appears to be fully evaluated before any part of the right-hand operand is evaluated.

如果该运算符是复合赋值运算符(第15.26.2节),则对左侧操作数的求值包括记住该左侧操作数所表示的变量以及获取并保存该变量的值以用于操作数.隐式二进制操作.

If the operator is a compound-assignment operator (§15.26.2), then evaluation of the left-hand operand includes both remembering the variable that the left-hand operand denotes and fetching and saving that variable's value for use in the implied binary operation.

可以在JLS的链接部分找到与您的问题类似的更多示例,例如:

More examples similar to your question can be found in the linked part of the JLS, such as:

示例15.7.1-1.首先评估左操作数

在以下程序中,*运算符具有一个左操作数,该操作数 包含一个变量的赋值和一个右手操作数 包含对相同变量的引用.由产生的价值 参考资料将反映出分配首先发生的事实.

In the following program, the * operator has a left-hand operand that contains an assignment to a variable and a right-hand operand that contains a reference to the same variable. The value produced by the reference will reflect the fact that the assignment occurred first.

class Test1 {
    public static void main(String[] args) {
        int i = 2;
        int j = (i=3) * i;
        System.out.println(j);
    }
}

该程序产生输出:

9

不允许*操作符的评估产生6 而不是9.

It is not permitted for evaluation of the * operator to produce 6 instead of 9.

这篇关于加法赋值+ =表达式中的行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆