Considere el siguiente ejemplo:
class Quirky {
public static void main(String[] args) {
int x = 1;
int y = 3;
System.out.println(x == (x = y)); // false
x = 1; // reset
System.out.println((x = y) == x); // true
}
}
No estoy seguro de si hay un elemento en la Especificación del lenguaje Java que dicta cargar el valor anterior de una variable para comparar con el lado derecho (x = y
) que, por el orden implicado entre paréntesis, debe calcularse primero.
¿Por qué la primera expresión evalúa a false
, pero la segunda evalúa a true
? Hubiera esperado (x = y)
ser evaluado primero, y luego se compararía x
consigo mismo ( 3
) y volvería true
.
Esta pregunta es diferente del orden de evaluación de subexpresiones en una expresión Java, ya que x
definitivamente no es una 'subexpresión' aquí. Debe cargarse para la comparación en lugar de ser 'evaluado'. La pregunta es específica de Java y la expresión x == (x = y)
, a diferencia de los constructos poco prácticos y extravagantes comúnmente diseñados para preguntas difíciles de la entrevista, provino de un proyecto real. Se suponía que era un reemplazo de una línea para el idioma de comparar y reemplazar
int oldX = x;
x = y;
return oldX == y;
que, siendo aún más simple que la instrucción x86 CMPXCHG, merecía una expresión más corta en Java.
fuente
x = y
es ciertamente relevante y causa el efecto secundario quex
se establece en el valor dey
.Respuestas:
No. Es un error común pensar que los paréntesis tienen algún efecto (general) en el orden de cálculo o evaluación. Solo fuerzan las partes de su expresión en un árbol en particular, vinculando los operandos correctos a las operaciones correctas para el trabajo.
(Y, si no los usa, esta información proviene de la "precedencia" y la asociatividad de los operadores, algo que es el resultado de cómo se define el árbol de sintaxis del lenguaje. De hecho, así es exactamente cómo funciona cuando usted usamos paréntesis, pero simplificamos y decimos que no confiamos en ninguna regla de precedencia entonces).
Una vez hecho esto (es decir, una vez que su código se ha analizado en un programa), esos operandos aún deben evaluarse, y hay reglas separadas sobre cómo se hace: dichas reglas (como Andrew nos ha mostrado) establecen que el LHS de cada operación se evalúa primero en Java.
Tenga en cuenta que este no es el caso en todos los idiomas; por ejemplo, en C ++, a menos que esté utilizando un operador de cortocircuito como
&&
o||
, el orden de evaluación de los operandos generalmente no está especificado y no debe confiar en él de ninguna manera.Los maestros deben dejar de explicar la precedencia del operador utilizando frases engañosas como "esto hace que la adición suceda primero". Dada una expresión,
x * y + z
la explicación adecuada sería "la precedencia del operador hace que la adición ocurra entrex * y
yz
, en lugar de entrey
yz
", sin mencionar ningún "orden".fuente
==
es un operador binario de igualdad .fuente
Como dijo LouisWasserman, la expresión se evalúa de izquierda a derecha. Y a Java no le importa lo que realmente hace "evaluar", solo le importa generar un valor (no volátil, final) para trabajar.
Entonces, para calcular la primera salida de
System.out.println()
, se hace lo siguiente:y para calcular el segundo:
Tenga en cuenta que el segundo valor siempre se evaluará como verdadero, independientemente de los valores iniciales de
x
yy
, porque está comparando efectivamente la asignación de un valor con la variable a la que está asignado y,a = b
yb
será, evaluado en ese orden, siempre será el mismo por definición.fuente
Ahi esta. La próxima vez que no tenga claro qué dice la especificación, lea la especificación y luego haga la pregunta si no está clara.
Esa afirmación es falsa. Los paréntesis no implican un orden de evaluación . En Java, el orden de evaluación es de izquierda a derecha, independientemente de los paréntesis. Los paréntesis determinan dónde están los límites de subexpresión, no el orden de evaluación.
La regla para el
==
operador es: evaluar el lado izquierdo para producir un valor, evaluar el lado derecho para producir un valor, comparar los valores, la comparación es el valor de la expresión.En otras palabras, el significado de
expr1 == expr2
siempre es el mismo que si hubiera escritotemp1 = expr1; temp2 = expr2;
y luego evaluadotemp1 == temp2
.La regla para el
=
operador con una variable local en el lado izquierdo es: evaluar el lado izquierdo para producir una variable, evaluar el lado derecho para producir un valor, realizar la asignación, el resultado es el valor asignado.Así que póngalo junto:
Tenemos un operador de comparación. Evalúe el lado izquierdo para producir un valor; obtenemos el valor actual de
x
. Evalúe el lado derecho: esa es una asignación, por lo que evaluamos el lado izquierdo para producir una variable, la variablex
, evaluamos el lado derecho, el valor actual dey
, se lo asignamosx
y el resultado es el valor asignado. Luego comparamos el valor original dex
con el valor asignado.Puedes hacer
(x = y) == x
como ejercicio. Nuevamente, recuerde, todas las reglas para evaluar el lado izquierdo suceden antes que todas las reglas para evaluar el lado derecho .Su expectativa se basa en un conjunto de creencias incorrectas sobre las reglas de Java. Esperemos que ahora tenga las creencias correctas y espere en el futuro cosas verdaderas.
Esta afirmación es falsa. Esa pregunta es totalmente pertinente.
Esta afirmación también es falsa. Es una subexpresión dos veces en cada ejemplo.
No tengo idea de lo que esto significa.
Aparentemente todavía tienes muchas creencias falsas. Mi consejo es que leas la especificación hasta que tus creencias falsas sean reemplazadas por creencias verdaderas.
La procedencia de la expresión no es relevante para la pregunta. Las reglas para tales expresiones se describen claramente en la especificación; Léelo!
Como ese reemplazo de una línea causó mucha confusión en usted, el lector del código, sugeriría que fue una mala elección. Hacer que el código sea más conciso pero más difícil de entender no es una victoria. Es poco probable que el código sea más rápido.
Por cierto, C # tiene comparar y reemplazar como un método de biblioteca, que puede ser reducido a una instrucción de máquina. Creo que Java no tiene dicho método, ya que no se puede representar en el sistema de tipos Java.
fuente
Está relacionado con la precedencia del operador y cómo se evalúa a los operadores.
Los paréntesis '()' tienen mayor prioridad y asociatividad de izquierda a derecha. La igualdad '==' viene después en esta pregunta y tiene asociatividad de izquierda a derecha. La tarea '=' es la última y tiene asociatividad de derecha a izquierda.
El sistema usa la pila para evaluar la expresión. La expresión se evalúa de izquierda a derecha.
Ahora viene a la pregunta original:
Primero x (1) será empujado a la pila. entonces interno (x = y) será evaluado y empujado a la pila con el valor x (3). Ahora x (1) se comparará con x (3), por lo que el resultado es falso.
Aquí, (x = y) será evaluado, ahora el valor de x se convierte en 3 yx (3) será empujado a la pila. Ahora x (3) con el valor cambiado después de la igualdad será empujado a la pila. Ahora se evaluará la expresión y ambos serán iguales, por lo que el resultado es verdadero.
fuente
No es lo mismo. El lado izquierdo siempre se evaluará antes que el lado derecho, y los corchetes no especifican un orden de ejecución, sino una agrupación de comandos.
Con:
Básicamente estás haciendo lo mismo que:
Y x tendrá el valor de y después de la comparación.
Mientras que con:
Básicamente estás haciendo lo mismo que:
Después x tomaron y 's valor. Y siempre volverá cierto .
fuente
En la primera prueba que estás verificando hace 1 == 3.
En la segunda prueba, su comprobación hace 3 == 3.
(x = y) asigna el valor y ese valor se prueba. En el primer ejemplo, x = 1 primero y luego se asigna x 3. ¿1 == 3?
En este último, a x se le asigna 3, y obviamente sigue siendo 3. ¿3 == 3?
fuente
Considere este otro ejemplo, quizás más simple:
Aquí, el operador de preincremento
++x
debe aplicarse antes de realizar la comparación, al igual que(x = y)
en su ejemplo, debe calcularse antes de la comparación.Sin embargo, la evaluación de la expresión todavía ocurre de izquierda a derecha , por lo que la primera comparación es en realidad
1 == 2
la segunda2 == 2
.Lo mismo sucede en tu ejemplo.
fuente
Las expresiones se evalúan de izquierda a derecha. En este caso:
fuente
Básicamente, la primera declaración x tenía su valor 1, por lo que Java compara 1 == con la nueva variable x que no será la misma
En el segundo dijiste x = y, lo que significa que el valor de x cambió y, por lo tanto, cuando lo vuelvas a llamar, será el mismo valor, por lo tanto, ¿por qué es verdad y x == x?
fuente
== es un operador de igualdad de comparación y funciona de izquierda a derecha.
aquí el antiguo valor asignado de x se compara con el nuevo valor asignado de x, (1 == 3) // falso
Mientras que aquí el nuevo valor de asignación de x se compara con el nuevo valor de retención de x asignado justo antes de la comparación, (3 == 3) // verdadero
Ahora considera esto
Por lo tanto, los paréntesis juegan su papel principal en las expresiones aritméticas, no solo en las expresiones de comparación.
fuente
x + (x = y)
y(x = y) + x
mostraría un comportamiento similar al original con operadores de comparación.La cuestión aquí es el orden de precedencia de los operadores aritmáticos / operadores relacionales de los dos operadores
=
frente==
al dominante es==
(los operadores relacionales dominan) ya que precede a los=
operadores de asignación. A pesar de la precedencia, el orden de evaluación es LTR (IZQUIERDA A DERECHA). La precedencia aparece en la imagen después del orden de evaluación. Entonces, independientemente de cualquier evaluación de restricciones, es LTR.fuente
Es fácil en la segunda comparación a la izquierda es la asignación después de asignar y a x (a la izquierda) y luego se compara 3 == 3. En el primer ejemplo, se compara x = 1 con la nueva asignación x = 3. Parece que siempre se toman declaraciones de lectura de estado actual de izquierda a derecha de x.
fuente
El tipo de pregunta que hizo es una muy buena pregunta si desea escribir un compilador de Java o probar programas para verificar que un compilador de Java funciona correctamente. En Java, estas dos expresiones deben producir los resultados que vio. En C ++, por ejemplo, no tienen que hacerlo, por lo que si alguien reutiliza partes de un compilador de C ++ en su compilador de Java, teóricamente podría encontrar que el compilador no se comporta como debería.
Como desarrollador de software, escribir código que sea legible, comprensible y mantenible, ambas versiones de su código se considerarían horribles. Para entender lo que hace el código, uno tiene que saber exactamente cómo se define el lenguaje Java. Alguien que escribe código Java y C ++ se estremecería al mirar el código. Si tiene que preguntar por qué una sola línea de código hace lo que hace, entonces debe evitar ese código. (Supongo y espero que los chicos que respondieron correctamente a su pregunta de "por qué" también eviten ese indicio de código).
fuente