一条线上的多个分配未按预期工作

Posted

技术标签:

【中文标题】一条线上的多个分配未按预期工作【英文标题】:Multiple assignment on one line not working as expected 【发布时间】:2015-11-21 13:44:09 【问题描述】:

我试图在示例中交换两个ints - xy,并在没有库函数的情况下在一行中完成。

所以我从这个开始:

int x = 4;
int y = 3;

System.out.println(x);
System.out.println(y);

x ^= y;

System.out.println(x);
System.out.println(y);

y ^= x;

System.out.println(x);
System.out.println(y);

x ^= y;

System.out.println(x);
System.out.println(y);

如预期的那样,输出为4, 3, 7, 3, 7, 4, 3, 4。到目前为止一切顺利。

接下来是这样的:

int x = 4;
int y = 3;

System.out.println(x);
System.out.println(y);

y ^= (x ^= y);

System.out.println(x);
System.out.println(y);

x ^= y;

System.out.println(x);
System.out.println(y);

再次如预期的那样输出4, 3, 7, 4, 3, 4。到目前为止还不错。

最后是这样的:

int x = 4;
int y = 3;

System.out.println(x);
System.out.println(y);

x ^= (y ^= (x ^= y));

System.out.println(x);
System.out.println(y);

在这个阶段,输出变成了4, 3, 0, 4。现在我知道04 ^ 4 的结果,因为当时x 分配还没有完成——为什么会这样?为什么x ^= y 不实际将7 分配给x 变量,使其成为最后一次分配的7 ^ 4

【问题讨论】:

【参考方案1】:

让我们尝试扩展您的最后一个表达式。

计算结果为,

x = x^(y = y^ (x = x^y));

请注意,表达式的计算顺序是从leftright

变成了,

x = 4 ^ (y = 3 ^ (x = 4 ^ 3));

现在,问题已经很明显了。对吧?

编辑:

为了消除混乱,让我试着解释一下我的意思是从左到右评估。

int i = 1;
s = i + (i = 2) + i;

现在,表达式将计算为,

s = 1 +    2    + 2;

请注意,分配左侧上的i1,但分配右侧(以及分配)被评估为2 , 因为从左到右求值,所以到表达式的第 2 和第 3 部分时,is 的值为2

【讨论】:

如果不是很明显,让我补充一下,有一个有效的单线解决方案可以解决 OP 的问题:x = (y = (x = x^y)^y)^x;。基本上只是把你的扩展形式放在上面并在每个级别交换左右操作数。 干杯,从左到右是有道理的,我只是在使用 '^=' 函数时从未考虑过它,因为我认为它是一个将作为一个步骤进行评估的单个操作,并检索值x 当时。很好的解释,谢谢。【参考方案2】:

评估顺序在 JLS 第 15 章中定义。项目15.7.1 说:

如果运算符是复合赋值运算符(第 15.26.2 节),则左侧操作数的评估包括记住左侧操作数表示的变量以及获取和保存该变量的值以用于隐含二元运算。

为了进一步解释,他们有两个涉及赋值的计算示例。这里的赋值在操作符的左边:

int i = 2;
int j = (i=3) * i;
System.out.println(j);

而且他们特意说结果是9,不允许是6。也就是说,(i=3)都计算为3,i在与自身相乘之前被赋值为3。

但在第二个例子中:

    int a = 9;
    a += (a = 3);  // first example
    System.out.println(a);
    int b = 9;
    b = b + (b = 3);  // second example
    System.out.println(b);

JLS 规定两个打印都应该产生 12,并且不允许产生 6。也就是说,因为对 b 的赋值是在右边,所以左边的值 b(或隐含的左边 @987654325 += 操作中的@),首先获取并保存该赋值之前的值,然后才执行括号内的操作。

在内部,表达式被分解为 JVM 操作,将值推送和弹出到“操作数堆栈”。如果你这样想——在b = b + (b=3)b的值首先被压入操作数堆栈,然后执行(b=3),然后将它的值添加到从堆栈中弹出的值( b) 的旧值,这将是有意义的。此时,左手b 只代表“b 入栈时的值是多少”,而不是“b 的当前值”。

【讨论】:

【参考方案3】:

让我们进行划分和计算。

最里面的括号首先执行,在所有括号解决后,表达式从左到右执行。

x ^= (y ^= (x ^= y));   // initial statement

x = x^(y = y^ (x = x^y)); //equals to 

() have the highest precedence

x = x^(y = y^ (x = 3^4)); // first highest precedence () 

x = x^(y = y ^ (x = 7)); // still the first x is 3

x = 4 ^(y = 3 ^ (x = 7));  // now 3 ^ 7 =4

x = 4 ^ 4;  // now  3 ^ 7 =4

x= 0;

【讨论】:

以上是关于一条线上的多个分配未按预期工作的主要内容,如果未能解决你的问题,请参考以下文章

C# arcengine 获取一条线上的点

[LintCode] Coins in a Line 一条线上的硬币

在另一条线上反向匹配多个场

Educational Codeforces Round 41

查看一个点是不是位于一条线上(向量)

检查 3 个点是不是在同一条线上