最近我遇到了这样一个问题:作业操作员链理解。
在回答这个问题时,我开始怀疑自己对加法赋值运算符=
或任何其他运算符=
(
我的问题是,下面表达式中的变量
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。
我们什么时候应该假设
一个
的值会在表达式的其他地方反映出来,什么时候不应该呢?
它只是使用了ops顺序的变化。
如果您需要操作顺序的提醒:
彭达斯:
P=括号
E=指数
MD=乘法/除法
AS=加法/减法
其余的从左到右。
这个变量只是从左到右读取,但是如果你看到一个括号在里面做所有的事情,并用一个常量替换它,那么继续。
第一前任:
var b=(a=(a=a))
var b=(1=(1=1))
var b=(1=2)
var b=3
第二个前:
var b=(a=a)(a=a)
var b=(1=1)(a=a)
var b=2(2=2)
var b=24
var b=6
var a = 1
var b = (a += (a += a))
console.log(b);
a = 1
b = (a += a) + (a += a)
console.log(b);
a = 1
b = a += a += a;
console.log(b);
以下是需要注意的规则
>
表达式求值
表达式1
a = 1
b = (a += (a += a))
b = (1 += (a += a)) // a = 1
b = (1 += (1 += a)) // a = 1
b = (1 += (1 += 1)) // a = 1
b = (1 += (2)) // a = 2 (here assignment is -> a = 1 + 1)
b = (3) // a = 3 (here assignment is -> a = 1 + 2)
表达式2
a = 1
b = (a += a) + (a += a)
b = (1 += a) + (a += a) // a = 1
b = (1 += 1) + (a += a) // a = 1
b = (2) + (a += a) // a = 2 (here assignment is -> a = 1 + 1)
b = (2) + (2 += a) // a = 2 (here here a = 2)
b = (2) + (2 += 2) // a = 2
b = (2) + (4) // a = 4 (here assignment is -> a = 2 + 2)
b = 6 // a = 4
表达式3
a = 1
b = a += a += a += a += a
b = 1 += 1 += 1 += 1 += 1 // a = 1
b = 1 += 1 += 1 += 2 // a = 2 (here assignment is -> a = 1 + 1)
b = 1 += 1 += 3 // a = 3 (here assignment is -> a = 1 + 2)
b = 1 += 4 // a = 4 (here assignment is -> a = 1 + 3)
b = 5 // a = 5 (here assignment is -> a = 1 + 4)
请记住,a=x
真正的意思是a=ax
。要理解的关键点是加法是从左到右求值的——也就是说,a x
中的a
在x
之前求值。
那么让我们看看b=(a=(a=a))
做了什么。首先,我们使用规则a=x
表示a=ax
,然后开始按照正确的顺序仔细计算表达式:
b=(a=a(a=a))
因为a=x
表示a=a x
b=(a=1(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
是2
b=(a=1 2)
因为a
现在是2
b=(a=3)
因为1 2
是3
b=3
因为a
现在是3
这就给我们留下了如上所述的a=3
和b=3
。
让我们用另一个表达式来试试,b=(a=a)(a=a)
:
b=(a=aa)(a=aa)
b=(a=11)(a=aa)
,记住我们先计算左项,再计算右项b=(a=2)(a=a)
b=2(a=aa)
而a
现在是2。开始评估正确的术语b=2(a=22)
b=2(a=4)
b=24
和a
现在是4
b=6
这就剩下a=4
和b=6
了。这可以通过在Java /JavaScript中打印出a
和b
来验证(两者在这里具有相同的行为)。
将这些表达式看作解析树也可能有所帮助。当我们评估a(bc)
时,在RHS(bc)
之前评估LHSa
。这在树结构中编码:
+
/ \
a +
/ \
b c
注意,我们不再有任何括号了——操作顺序被编码到树结构中。当我们评估树中的节点时,我们按照固定的顺序处理节点的子节点(即从左到右处理)。例如,当我们处理根节点
时,我们在右子树
(bc)
之前计算左子树a
,而不管右子树是否包含在括号中(因为括号甚至不在解析树中)。
正因为如此,Java/JavaScript并不总是首先计算“最嵌套的括号”,这与您可能学习的算术规则不同。
请参阅Java语言规范:
Java编程语言保证运算符的操作数以特定的计算顺序计算,即从左到右。
...
在计算右操作数的任何部分之前,二元运算符的左操作数似乎已被完全计算。
如果运算符是复合赋值运算符(§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。
问题内容: 最近我遇到了一个问题:赋值运算符链理解。 在回答这个问题,我开始怀疑我自己的加法赋值运算符的行为的理解或任何其他(,,等)。 我的问题是,下面的表达式中的变量何时更新到位,以便其更改的值在求值过程中反映在表达式的其他位置,其背后的逻辑是什么?请看以下两个表达式: 表达式1 表达式2 在第一个表达式中,当计算最里面的表达式时,似乎不更新的值,因此结果是而不是。 但是,在第二个表达式中,的
问题内容: 我正在尝试使用附加到的方式更新我的模型。 我有一个赋值表达式没有问题 外 的,或者与调用方法的范围 内 的。不过,如果我使用赋值 里面 的,它似乎被忽略。我没有在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编译器。太阳会说