uma - objetos java




Lógica incremental (5)

Citando Especificação da Linguagem Java, 15.7 Ordem de Avaliação :

A linguagem de programação Java garante que os operandos dos operadores pareçam ser avaliados em uma ordem de avaliação específica, ou seja, da esquerda para a direita .

O operando esquerdo de um operador binário parece ser totalmente avaliado antes que qualquer parte do operando direito seja avaliada.

Se o operador for um operador de atribuição de compostos ( §15.26.2 ), a avaliação do operando à esquerda inclui tanto a lembrança da variável que o operando da esquerda indica quanto a busca e o salvamento do valor dessa variável para uso na operação binária implícita .

Então, essencialmente, i += ++i lembrará do valor antigo de i no lado esquerdo, antes de avaliar o lado direito.

Lembre-se, a ordem de avaliação dos operandos e a precedência dos operadores são duas coisas diferentes.

Mostrando a ordem de avaliação, passo a passo, com o valor salvo em {chaves}:

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

Acompanhamento de edições para questionar

Se nomearmos o valor inicial I e a constante N :

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

Então podemos ver que os valores são:

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;

Eu estou tentando ir mais fundo com post e pre incrementors mas estou um pouco preso com a seguinte expressão:

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
}

Eu sei que estou perdendo a lógica em algum lugar, mas onde?

O que eu tentei:

  • Indo da esquerda para a direita (embora eu saiba que não é recomendado)
  • Indo do suporte mais insidestro e a partir daí.

Obrigado pela ajuda

PS: Os comentários são os detalhes do meu cálculo

EDITAR 1

Eu tentei mudar o valor codificado da expressão de 2 para outra coisa e o resultado sempre dá 0

Veja este exemplo:

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

Esta expressão deve logicamente estar longe de 0 mas de alguma forma é impressa.

O mesmo acontece quando eu uso um negativo:

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

EDIT 2

Agora, mudei o valor de i para começar:

    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

Dá o dobro de cada vez, independentemente do valor codificado.


Esta é a lógica levando em conta sua primeira edição (com um X desconhecido):

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
}

Truques aqui:

Para sua segunda edição (com um desconhecido I adicionei):

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
}

Ok, vamos detalhar tudo:

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

Então, começando pelo parêntese mais interno:

(i+=2 + --i)
  • ele primeiro decrementa i e usa o resultado ( -1 )
  • em seguida, adicione 2 ( -1+2=1 )
  • e adicione o resultado a i (que é 0 no início da operação) ( 0+1=1=i )

No final, o primeiro decremento é ignorado pela reatribuição.

Próximo parêntese:

i+= (++i + previous_result - ++i)
  • aumenta o i (com ++i ) em dois pontos
  • então a operação se torna (i+1) + previous_result - (i+2) (observe como o incremento não é simultâneo) que dá 2 + 1 - 3 = 0 .
  • o resultado da operação é adicionado ao inicial i ( 0 )

Novamente, o incremento será descartado pela reatribuição.

finalmente:

i = previous_result

O que dá 0 :)


Paranthesis toma a precedência. fim seria do interior mais para ultraperiféricas

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

Cada expressão é avaliada como 0


Sugiro o seguinte: formate o código de forma diferente, de modo que haja apenas 1 instrução por linha, por exemplo

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

Em seguida, execute-o sob o depurador e pressione F5 ("Step into") sempre. Isso ajudará você a entender em qual ordem os itens são avaliados:

  1. int i=0;
  2. i= : ... (precisa esperar pelo resultado do cálculo A)
  3. i+= ... (precisa esperar B)
  4. ++i : i = 1
  5. i+= ... (precisa esperar C)
  6. 2+
  7. --i : i = 0
  8. ...: i = 3 (resultado para esperar C)
  9. -
  10. ++i : i = 4 e operando de - também é 4
  11. ...: i = 0 (resultado para esperar B)
  12. ...: i = 0 (resultado da espera A)

A linha 10 sempre fará o resultado da linha 3 0 , então o valor inicial de i nunca será alterado por toda a operação.





pre-increment