当前位置: 首页 > 面试题库 >

加法赋值+ =表达式中的行为

颜祖鹤
2023-03-14
问题内容

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

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

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

表达式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

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

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


问题答案:

记住那a += x真的意味着a = a + x。要理解的关键点是 加法是从左到右求值的, 也就是说,ain a + x是在之前求值的x

因此,让我们弄清楚是什么b = (a += (a += a))。首先,我们使用规则a += xmean a = a + x,然后开始以正确的顺序仔细评估表达式:

  • 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

这使我们有了a = 3b = 3上面所解释的。

让我们用另一个表达式尝试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

这给我们留下了a = 4b = 6。可以通过打印输出a以及b使用Java / JavaScript 进行验证(两者在此处具有相同的行为)。

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

   +
  / \
 a   +
    / \
   b   c

请注意,我们再也没有括号了-操作的顺序被编码到树形结构中。当我们评估树中的节点时,我们以 固定的顺序
处理该节点的子节点(即,从到左到右+)。例如,当我们处理根节点时+,我们先评估左子树,a然后评估右子树(b + c),而不管右子树是否用括号括起来(因为括号甚至不存在于解析树中)。

因此,与您可能被教过算术规则的规则相反,Java / JavaScript 并不 总是首先评估“最嵌套的括号”。

请参阅Java语言规范:

15.7。评估单

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

15.7.1。首先评估左手操作数

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

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

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

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

在下面的程序中,*运算符具有一个左操作数和一个右操作数,左操作数包含对变量的赋值,右操作数包含对相同变量的引用。由引用产生的值将反映分配首先发生的事实。

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

该程序产生输出:

9

不允许评估*运算符产生6而不是9。



 类似资料:
  • 最近我遇到了这样一个问题:作业操作员链理解。 在回答这个问题时,我开始怀疑自己对加法赋值运算符或任何其他(

  • 问题内容: 我正在尝试使用附加到的方式更新我的模型。 我有一个赋值表达式没有问题 外 的,或者与调用方法的范围 内 的。不过,如果我使用赋值 里面 的,它似乎被忽略。我没有在Firefox控制台中看到任何消息报告,但是还没有尝试设置断点来查看是否触发了该事件。 小提琴在这里,如果您愿意的话(还有几个早期的变体)。 问题答案: 指令ngRepeat为每次迭代创建一个新作用域,因此您需要在父作用域中引

  • 如果我们看一下C标准草案第5.1.2节,第2段说: 对lambda表达式的求值会产生一个prvalue临时(12.2)。这个临时称为闭包对象。lambda表达式不得出现在未求值的操作数中(第5条)。[注意:闭包对象的行为类似于函数对象(20.8)。-结束注释] 和节常量表达式第2段说: 条件表达式是核心常量表达式,除非它涉及以下其中一个作为潜在求值子表达式(3.2),但不考虑未求值的逻辑AND(5

  • PEP 572引入了为Python 3.8实现的赋值表达式(俗称Walrus操作符)。这似乎是一个非常重要的新特性,因为它将允许在理解和lambda函数中进行这种形式的赋值。 赋值表达式的语法、语义学和语法规范到底是什么? 为什么在PEP 379中关于“添加赋值表达式”的类似想法之前遭到拒绝的情况下,引入了这个新的(似乎相当激进的概念)?

  • 问题内容: 我正在使用Java,但出现错误插入“赋值运算符表达式”以完成表达式” 该错误的含义是什么?是否有所有Java错误消息的列表及其含义? 谢谢 问题答案: 根据此页面,您正在执行此操作 编写没有赋值运算符的赋值语句: Eclipse: 语法错误,插入“ AssignmentOperator表达式”以完成表达式 您应该知道编译错误消息与实现有关。以上适用于Eclipse编译器。太阳会说