【问题标题】:Incrementor logic增量器逻辑
【发布时间】:2016-01-12 06:23:09
【问题描述】:

我正在尝试更深入地使用 post 和 pre 增量器,但我有点坚持以下表达式:

public static void main(String[] args) {
    int i = 0;
    i = i+=(++i + (i+=2 + --i) - ++i);
    // i = 0 + (++i + (i+=2 + --i) - ++i);
    // i = 0 + (1   + (3    +   2) -  1 );
    // i = 0 + (6                  -  1 );
    System.out.println(i); // Prints 0 instead of 5
}

我知道我在某处遗漏了逻辑,但在哪里?

我尝试过的:

  • 从左到右(虽然我知道不推荐)
  • 从内侧括号开始,从那里开始。

感谢您的帮助

PS : cmets 是我的微积分的细节

编辑 1

我尝试将表达式中的硬编码值从2 更改为其他值,结果总是给出0

看看这个例子:

    int i = 0;
    i = i+=(++i + (i+=32500 + --i) - ++i);
    System.out.println(i); // Prints 0

这个表达式在逻辑上应该离0很远,但它确实打印了它。

当我使用否定时也会发生同样的情况:

    int i = 0;
    i = i+=(++i + (i+=(-32650) + --i) - ++i);
    System.out.println(i); // Prints 0

编辑 2

现在,我将i 的值改为:

    int i = 1;
    i = i+=(++i + (i+=2 + --i) - ++i);
    System.out.println(i); // Prints 2
    
    i = 2;
    i = i+=(++i + (i+=10000 + --i) - ++i);
    System.out.println(i); // Prints 4
    
    i = 3;
    i = i+=(++i + (i+=(-32650) + --i) - ++i);
    System.out.println(i); // Prints 6

无论硬编码的值是什么,它每次都会给出i 的两倍。

【问题讨论】:

  • 啊,没听懂
  • 从左到右不会解决它.. 编译器将普通表达式转换为 reverse polish notations 并评估它们,然后构造一个 表达式树 并解决表达式.
  • 所以简单地从左到右或从内到内不会给你程序将生成的结果
  • @Thomas 这确实只是好奇。我想了解我所学的一切。
  • @JiriTousek 我用 1.4 编译,然后运行以下版本,在 Windows 7 上都生成了0: 1.4.2_19, 1.5.0_22, 1.6.0_45, 1.7.0_79, 1.8.0_51 , Sun/Oracle JVM。

标签: java logic post-increment pre-increment


【解决方案1】:

引用Java Language Specification, 15.7 Evaluation Order:

Java 编程语言保证运算符的操作数似乎以特定的计算顺序进行计算,即从从左到右

二元运算符的左侧操作数似乎在右侧操作数的任何部分被评估之前被完全评估

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

所以,基本上,i += ++i 会记住左侧的旧值 i评估右侧之前。

请记住,操作数的求值顺序和运算符的优先级是两个不同的东西。

逐步显示评估顺序,并在 {braces} 中保存值:

int i = 0;
i    = i    += (++i + (i    += 2 + --i) - ++i); // i = 0
i{0} = i    += (++i + (i    += 2 + --i) - ++i); // i = 0
i{0} = i{0} += (++i + (i    += 2 + --i) - ++i); // i = 0
i{0} = i{0} += (1   + (i    += 2 + --i) - ++i); // i = 1
i{0} = i{0} += (1   + (i{1} += 2 + --i) - ++i); // i = 1
i{0} = i{0} += (1   + (i{1} += 2 + 0  ) - ++i); // i = 0
i{0} = i{0} += (1   + (i{1} += 2      ) - ++i); // i = 0
i{0} = i{0} += (1   + 3                 - ++i); // i = 3
i{0} = i{0} += (4                       - ++i); // i = 3
i{0} = i{0} += (4                       - 4  ); // i = 4
i{0} = i{0} += 0                              ; // i = 4
i{0} = 0                                      ; // i = 0
0                                             ; // i = 0

跟进问题的修改

如果我们将初始值命名为I,并将常量命名为N

int i = I;
i = i += (++i + (i += N + --i) - ++i);

那么我们可以看到这些值为:

i{I} = i{I} += ((I+1) + (i{I+1} += N + I) - ((I+1+N+I)+1));
i{I} = i{I} += (I + 1 + (I + 1 + N + I) - (I + 1 + N + I + 1));
i{I} = i{I} += (I + 1 + I + 1 + N + I - I - 1 - N - I - 1);
i{I} = i{I} += I;
i{I} = I + I;
i = 2 * I;

【讨论】:

  • 你好安德烈亚斯!这似乎确实是一个可以接受的答案,但是当 i = 其他东西以及硬编码值大于 2 时,您会介意(如果没有太多要求的话)应用相同的逻辑吗?那太棒了,我尝试这样做但答案错误
  • @YassinHajaj 就是这样做的。 :-)
  • 非常感谢!太好了!
【解决方案2】:

这是考虑到您的第一次编辑的逻辑(使用未知的X):

public static void main(String[] args) {
    int i = 0;
    i = i+=(++i + (i+=X + --i) - ++i);
    // i = 0 += (++i + ((i += (X + --i)) - ++i));
    // i = 0 += (1 + ((i += (X + --i)) - ++i)); // i = 1
    // i = 0 += (1 + ((1 += (X + --i)) - ++i)); // i = 1 and i will then take the result of 1 += (X + --i)
    // i = 0 += (1 + ((1 += (X + 0)) - ++i)); // i = 0 and i will then take the result of 1 += (X + 0)
    // i = 0 += (1 + (X + 1 - ++i)); // i = X + 1
    // i = 0 += (1 + (X + 1 - X - 2)); // i = X + 2
    // i = 0 += (0); // i = X + 2
    // i = 0;
    System.out.println(i); // Prints 0
}

技巧在这里:

第二次编辑(添加了未知的I):

public static void main(String[] args) {
    int i = I;
    i = i+=(++i + (i+=X + --i) - ++i);
    // i = I += (++i + ((i += (X + --i)) - ++i));
    // i = I += (I+1 + ((i += (X + --i)) - ++i)); // i = I+1
    // i = I += (I+1 + ((I+1 += (X + --i)) - ++i)); // i = I+1 and i will then take the result of I+1 += (X + --i)
    // i = I += (I+1 + ((I+1 += (X + I)) - ++i)); // i = I and i will then take the result of I+1 += (X + I)
    // i = I += (I+1 + (X+2*I+1 - ++i)); // i = X + 2*I + 1
    // i = I += (I+1 + (X+2*I+1 - X-2*I-2)); // i = X + 2*I + 2
    // i = I += (I); // i = X + 2*I + 2
    // i = 2 * I;
    System.out.println(i); // Prints 2 * I
}

【讨论】:

  • 按照这个逻辑,你能详细说明为什么i = i+= (++i + (i+=2)) == i = i+= (++i + (i+=2 + --i)); = 4
  • 我正在编辑这个问题,因为我遇到了一些非常奇怪的事情!
  • 嘿@Tunaki,你能检查一下我对帖子所做的编辑吗?这个逻辑不再适用
【解决方案3】:

我建议如下:以不同的方式格式化代码,以便每行只有 1 条语句,例如

@Test
public void test() {
    int i = 0;
    i = 
    i+=
    (
    ++i 
    + 
    (
    i+=
    2 
    + 
    --i
    ) 
    -
    ++i
    );
    System.out.println(i); // Prints 0 instead of 5
}

然后在调试器下运行它并始终按 F5(“Step into”)。这将帮助您了解评估项目的顺序:

  1. int i=0;
  2. i=: ...(需要等待A的计算结果)
  3. i+= ...(需要等待B)
  4. ++i:i=1
  5. i+= ...(需要等待C)
  6. 2+
  7. --i:i=0
  8. ...: i=3(等待 C 的结果)
  9. -
  10. ++i: i=4 并且 - 的操作数也是 4
  11. ...: i=0(等待 B 的结果)
  12. ...:i=0(等待 A 的结果)

第 10 行总是会得到第 3 行的结果0,所以整个操作永远不会改变 i 的初始值。

【讨论】:

    【解决方案4】:

    好的,让我们分解所有内容:

    int i = 0; // i = 0, no big deal
    

    然后从最里面的括号开始:

    (i+=2 + --i)
    
    • 它首先递减 i 并使用结果 (-1)
    • 然后加 2 (-1+2=1)
    • 并将结果添加到 i(在操作开始时为 0)(0+1=1=i

    最后,重新分配忽略第一个减量。

    下一个括号:

    i+= (++i + previous_result - ++i)
    
    • 在两点增加i(与++i
    • 然后操作变为(i+1) + previous_result - (i+2)(注意增量不是同时的),得到2 + 1 - 3 = 0
    • 将运算结果添加到初始i (0)

    再次分配,增量将被重新分配丢弃。

    终于:

    i = previous_result
    

    给出 0 :)

    【讨论】:

    • 我正在编辑这个问题,因为我遇到了一些非常奇怪的事情!
    【解决方案5】:

    我追踪了 i 的值,结果如下:

    i = i+=(++i + (i+=2 + --i) - ++i);
    initialization: i = 0;
    ++i: i = 1;(1st one) and store this value
    (i+=2 + --i): In it
     --i: i = 0;(i was changed by the previous ++i)
     i += 2 + 0: i = 2;(value of the inner (i+=2 + --i), store it)
    ++i: i = 3;
    1 + 2 -3: i = 0;
    i += 0: i = 0;
    

    左边第二个i的值不为零,是右边的所有操作都完成后的i的值。

    【讨论】:

      【解决方案6】:

      因为最高优先级 (...) 将首先被评估,然后是 ++ & --,然后是其余的运算符。 你的表情是这样的

      i = i += ( (++i) + (i+=2 + (--i)) - (++i) );
      
      //i = i = i + ( (++i) + (i+=2 + (--i)) - (++i) );
      //i = i = 0 + ( (++i) + (i+=2 + (--i)) - (++i) );
      //i = i = 0 + ( (1) + (i+=2 + (--i)) - (++i) );
      //i = i = 0 + ( (1) + (i+=2 + (0)) - (++i) );
      //i = i = 0 + ( (1) + (2 + (0)) - (++i) );
      //i = i = 0 + ( (1) + (2) - (++i) );
      //i = i = 0 + ( (1) + (2) - (3) );
      //i = i = 0 + ( 3 - 3 );
      //i = i = 0 + ( 0 );
      //i = 0
      

      请用括号将您的表达完全括起来。您将了解表达式的评估顺序。

      为您的 EDIT 1

      i = i+=( (++i) + (i+=32500 + (--i) ) - (++i) ); 
      
      // 0 + ( (++i) + (i+=32500 + (--i) ) - (++i) ); // i = 0
      // 0 + ( (1) + (i+=32500 + (--i) ) - (++i) );  // i = 1
      // 0 + ( (1) + (i+=32500 + (0) ) - (++i) ); // i = 0
      // 0 + ( (1) + (32500 + (0) ) - (++i) );  // i = 32500
      // 0 + ( (1) + (32500) - (++i) ); // i = 32500
      // 0 + ( (1) + (32500) - (32501) ); // i = 32501
      // 0 + ( 32501 - 32501 ); // i = 32501
      // 0 // i = 0
      

      【讨论】:

        【解决方案7】:

        括号优先。顺序是从最里面到最外面

        (i+=2 + --i) =>i=i+=2==(2) --2 =0 
        (++i - ++i)  =>1-1=0 
        i=i+ =0+0 =0
        

        每个表达式的计算结果为 0

        【讨论】:

          猜你喜欢
          • 2021-10-29
          • 2017-01-04
          • 1970-01-01
          • 2020-07-30
          • 1970-01-01
          • 2015-10-03
          • 1970-01-01
          • 2015-09-20
          • 2018-10-06
          相关资源
          最近更新 更多