¿Por qué los operadores lógicos en JavaScript son asociativos?


Los operadores lógicos AND y OR son los únicos operadores perezosos en JavaScript junto con el operador condicional ternario . Se prueban para la evaluación del cortocircuito utilizando las siguientes reglas:

false && anything === false
true || anything === true

Esta es la misma manera en que se implementa en Haskell:

(&&) :: Bool -> Bool -> Bool
False && _ = False
True  && x = x

(||) :: Bool -> Bool -> Bool
True  || _ = True
False || x = x

Sin embargo según MDN los operadores lógicos en JavaScript se dejan asociativos. Esto es contra intuitivo. En mi humilde opinión deberían tener razón asociativo. Haskell hace lo correcto. Los operadores lógicos en Haskell son asociativos a la derecha:

infixr 3 &&
infixr 2 ||

Considere la siguiente expresión en Haskell:

False && True && True && True

Porque && es asociativo derecho en Haskell la expresión anterior es equivalente a:

False && (True && (True && True))

Por lo tanto no importa lo que la expresión (True && (True && True)) evalúa. Debido a la primera False la expresión completa se reduce a False en un solo paso.

Ahora considere lo que sucedería si && fuera izquierda asociativa. La expresión sería equivalente a:

((False && True) && True) && True

Ahora se necesitarían 3 reducciones para evaluar la expresión completa:

((False && True) && True) && True
(False && True) && True
False && True
False

Como puede ver, tiene más sentido que los operadores lógicos sean asociativos a la derecha. Esto me lleva a mi pregunta real:

¿Por qué los operadores lógicos en JavaScript son asociativos a la izquierda? ¿Qué tiene que decir la especificación ECMAScript sobre esto? ¿Son los operadores lógicos en JavaScript realmente asociativos? ¿Los documentos de MDN ¿tiene información incorrecta sobre la asociatividad de los operadores lógicos?


Editar: De acuerdo con la especificación los operadores lógicos se dejan asociativos:

LogicalANDExpression = BitwiseORExpression
                     | LogicalANDExpression && BitwiseORExpression

LogicalORExpression = LogicalANDExpression
                    | LogicalORExpression || LogicalANDExpression
Author: Aadit M Shah, 2013-12-15

3 answers

Para cualquier compilador decente, la asociatividad elegida de estos operadores es bastante irrelevante, y el código de salida será el mismo independientemente. Sí, el árbol de análisis es diferente, pero el código emitido no tiene que serlo.

En todos los lenguajes de la familia C que conozco (a la que también pertenece Javascript), los operadores lógicos se dejan asociativos. Así que la verdadera pregunta es, ¿por qué los lenguajes C-like definen a los operadores lógicos como asociativos de izquierda? Desde el elegido la asociatividad es irrelevante (en términos de semántica, y en términos de eficiencia), mi sospecha es que la asociatividad más "natural" (como en "lo que la mayoría de los otros operadores usan") fue elegida, aunque no tengo ninguna fuente para respaldar mis afirmaciones. Otra explicación posible es que los operadores asociativos izquierdos toman menos espacio de pila para analizar usando un analizador LALR (que no es una gran preocupación hoy en día, pero probablemente lo era cuando apareció C).

 13
Author: Pedro Rodrigues,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-17 19:10:26

Considere este código:

console.log(   true || (false && true) );   // right associative (implicit)
console.log(  (true || false) && true  );   // left associative (Javascript)

Ambos ejemplos de hecho devuelven el mismo resultado, pero esa no es la razón para preocuparse por la asociatividad del operador. La razón por la que es relevante aquí es debido a la forma única en que los operadores lógicos determinan sus resultados. A pesar de que todas las permutaciones finalmente hacen la misma conclusión final, el orden de los cálculos cambia, y eso puede tener grandes implicaciones para su código.

Así que, ahora, considere esto:

    var name = "";

    // ----------------------------------------
    // Right associative
    // ----------------------------------------
    name = "albert einstein";
    console.log("RIGHT : %s : %s", true || (false && updateName()), name);

    // ----------------------------------------
    // Left Associative
    // ----------------------------------------
    name = "albert einstein";
    console.log("LEFT  : %s : %s", (true || false) && updateName(), name);

    function updateName() {
        name = "charles manson";
        return true;
    }

La salida es:

    RIGHT : true : albert einstein
    LEFT  : true : charles manson

Ambas expresiones devuelven true, pero solo la versión asociada a la izquierda tenía que llamar a updateName() para devolver una respuesta. La versión correcta asociada es diferente. Solo evalúa el primer argumento en (false && updateName()) porque el segundo argumento no puede cambiar el false en un true.

Recuerda estos dos puntos:

  • La precedencia del operador describe el orden de anidamiento de las expresiones compuestas de diferentes tipos de operadores.
  • La asociatividad del operador describe el orden de anidamiento de las expresiones compuestas con la misma precedencia del operador .

Observe que ninguno de los dos puntos anteriores cambia la forma en que se interpreta una expresión individual, solo la forma en que se interpretan las expresiones compuestas. La asociatividad ocurre a un nivel superior.

La asociatividad del operador, así como la precedencia del operador, tienen un enorme impacto en cómo un lenguaje comportar. Comprender cuáles son esas diferencias es fundamental para poder usar y comprender rápidamente las diferencias funcionales en diversos lenguajes de programación. Definitivamente tienes un pulgar hacia arriba de mí para su pregunta y espero que esto aclara las cosas un poco. Cuidar.

 6
Author: drankin2112,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-01 15:50:56

Respuesta simple: Imagine var i = null; if (i == null || i.getSomeValue()) ... Cuando no se deja asociativo, la segunda prueba sería evaluada primero dándole una excepción.

 -1
Author: Peter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-23 22:54:00