javascript java language-lawyer compound-assignment

javascript - Asignación de suma+= comportamiento en expresión



language-lawyer compound-assignment (3)

Recientemente me encontré con esta pregunta: Entendimiento de cadena de operador de asignación .

Mientras respondía esta pregunta, empecé a dudar de mi propia comprensión del comportamiento del operador de asignación de suma += o cualquier otro operator= ( &= , *= , /= , etc.).

Mi pregunta es: ¿cuándo se actualiza la variable a en las expresiones a continuación para que su valor cambiado se refleje en otros lugares en la expresión durante la evaluación, y cuál es la lógica detrás de esto? Por favor, eche un vistazo a las siguientes dos expresiones:

Expresión 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

Expresión 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

En la primera expresión, cuando se evalúa la expresión más interna (a += a) , parece que no actualiza el valor de a , por lo que el resultado sale como 3 lugar de 4 .

Sin embargo, en la segunda expresión, el valor de a se actualiza y, por lo tanto, el resultado es 6.

¿Cuándo debemos asumir que el valor de un se reflejará en otros lugares en la expresión y cuándo no?


A continuación se detallan las reglas que deben ser atendidas.

  • Precedencia del operador
  • Asignación variable
  • evaluación de expresiones

    Expresión 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)

    Expresión 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

    Expresión 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)


Recuerda que a += x realmente significa a = a + x . El punto clave a comprender es que la adición se evalúa de izquierda a derecha , es decir, la a en a + x se evalúa antes de x .

Entonces, averigüemos qué hace b = (a += (a += a)) . Primero usamos la regla a += x significa a = a + x , y luego comenzamos a evaluar la expresión cuidadosamente en el orden correcto:

  • b = (a = a + (a = a + a)) porque a += x significa a = a + x
  • b = (a = 1 + (a = a + a)) porque a es actualmente 1 . Recuerde que evaluamos el término izquierdo a antes del término correcto (a = a + a)
  • b = (a = 1 + (a = 1 + a)) porque a sigue siendo 1
  • b = (a = 1 + (a = 1 + 1)) porque a sigue siendo 1
  • b = (a = 1 + (a = 2)) porque 1 + 1 es 2
  • b = (a = 1 + 2) porque a es ahora 2
  • b = (a = 3) porque 1 + 2 es 3
  • b = 3 porque a es ahora 3

Esto nos deja con a = 3 y b = 3 como se explicó anteriormente.

Intentemos esto con la otra expresión, b = (a += a) + (a += a) :

  • b = (a = a + a) + (a = a + a)
  • b = (a = 1 + 1) + (a = a + a) , recuerde que evaluamos el término izquierdo antes del derecho
  • b = (a = 2) + (a = a + a)
  • b = 2 + (a = a + a) y a ahora es 2. Comience a evaluar el término correcto
  • b = 2 + (a = 2 + 2)
  • b = 2 + (a = 4)
  • b = 2 + 4 y a ahora es 4
  • b = 6

Esto nos deja con a = 4 y b = 6 . Esto se puede verificar imprimiendo tanto a como b en Java / JavaScript (ambos tienen el mismo comportamiento aquí).

También podría ayudar pensar estas expresiones como árboles de análisis. Cuando evaluamos a + (b + c) , LHS a se evalúa antes que RHS (b + c) . Esto está codificado en la estructura de árbol:

+ / / a + / / b c

Tenga en cuenta que ya no tenemos paréntesis, el orden de las operaciones se codifica en la estructura de árbol. Cuando evaluamos los nodos en el árbol, procesamos los hijos del nodo en un orden fijo (es decir, de izquierda a derecha para + ). Por ejemplo, cuando procesamos el nodo raíz + , evaluamos el subárbol izquierdo a antes del subárbol derecho (b + c) , independientemente de si el subárbol derecho está entre paréntesis o no (ya que los paréntesis ni siquiera están presentes en el árbol parse).

Debido a esto, Java / JavaScript no siempre evalúa los "paréntesis más anidados" primero, en contraste con las reglas que se podrían haber enseñado para la aritmética.

Ver la especificación del lenguaje Java :

15.7. Orden de evaluacion

El lenguaje de programación Java garantiza que los operandos de los operadores parecen ser evaluados en un orden de evaluación específico, es decir, de izquierda a derecha.
...

15.7.1. Evaluar el operando de la mano izquierda primero

El operando de la izquierda de un operador binario parece estar completamente evaluado antes de que se evalúe cualquier parte del operando de la derecha.

Si el operador es un operador de asignación compuesta (§15.26.2), entonces la evaluación del operando de la izquierda incluye tanto recordar la variable que el operando de la izquierda denota como recuperar y guardar el valor de esa variable para usar en la operación binaria implícita .

Se pueden encontrar más ejemplos similares a su pregunta en la parte vinculada del JLS, como:

Ejemplo 15.7.1-1. El operando de la mano izquierda se evalúa primero

En el siguiente programa, el operador * tiene un operando a la izquierda que contiene una asignación a una variable y un operando a la derecha que contiene una referencia a la misma variable. El valor producido por la referencia reflejará el hecho de que la asignación ocurrió primero.

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

Este programa produce la salida:

9

No está permitido para la evaluación del operador * producir 6 en lugar de 9.


Solo usa una variación de orden de operaciones.

Si necesita un recordatorio de orden de operaciones:

PEMDAS:

P = paréntesis

E = Exponentes

MD = Multiplicación / División

AS = Suma / Resta

El resto de izquierda a derecha.

Esta variación solo se lee de izquierda a derecha, pero si ve un paréntesis, haga todo lo que está dentro de él, y reemplácelo con una constante y luego continúe.

Primero ex

var b = (a+=(a+=a))

var b = (1+=(1+=1))

var b = (1+=2)

var b = 3

Segundo ex:

var b = (a+=a)+(a+=a)

var b = (1+=1)+(a+=a)

var b = 2 + (2+=2)

var b = 2 + 4

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);

El último b = a += a += a ya que no hay paréntesis, se convierte automáticamente en b = 1 += 1 += 1 que es b = 3