¿Por qué no hay xor lógico en JavaScript?

Respuestas:

358

JavaScript rastrea su ascendencia hasta C, y C no tiene un operador XOR lógico. Principalmente porque no es útil. Bitwise XOR es extremadamente útil, pero en todos mis años de programación nunca he necesitado un XOR lógico.

Si tiene dos variables booleanas, puede imitar XOR con:

if (a != b)

Con dos variables arbitrarias que podría usar !para obligarlos a valores booleanos y luego usar el mismo truco:

if (!a != !b)

Sin embargo, eso es bastante oscuro y sin duda merecería un comentario. De hecho, incluso podría usar el operador XOR bit a bit en este punto, aunque esto sería demasiado inteligente para mi gusto:

if (!a ^ !b)
John Kugelman
fuente
El único problema con !=es que no puedes hacer lo mismo a ^= b, porque a !== bes solo el operador de desigualdad estricta .
mcpiroman
79

Javascript tiene un operador XOR bit a bit: ^

var nb = 5^9 // = 12

Puede usarlo con booleanos y dará el resultado como 0 o 1 (que puede convertir de nuevo a booleano, por ejemplo result = !!(op1 ^ op2)). Pero como dijo John, es equivalente a result = (op1 != op2), que es más claro.

Pikrass
fuente
28
Eso es bit XOR, no XOR lógico
Ismail Badawi
66
Puedes usarlo como un xor lógico. true^truees 0 y false^truees 1.
Pikrass
14
@Pikrass Puede usarlo como operador lógico en booleanos , pero no en otros tipos. ||y &&puede usarse como operadores lógicos en no booleanos (por ejemplo, 5 || 7devuelve un valor verdadero, "bob" && nulldevuelve un valor falsey) pero ^no puede. Por ejemplo, 5 ^ 7es igual a 2, que es verdad.
Mark Amery
10
@Pikrass Pero lamentablemente, (true ^ false) !== truelo que lo hace molesto con las bibliotecas que requieren booleanos reales
Izkata
2
@Pikrass Nunca debe usarlo como operador lógico en booleano porque la implementación depende del sistema operativo. Estaba usando algún tipo de a ^= truealternar booleanos y falla en algunas máquinas como los teléfonos.
Masadow
30

No hay operadores booleanos lógicos reales en Javascript (aunque se !acerca bastante). Un operador lógico solo tomaría trueo falsecomo operandos y solo devolvería trueo false.

En Javascript &&y ||tomar todo tipo de operandos y devolver todo tipo de resultados divertidos (lo que sea que se introduce en ellos).

Además, un operador lógico siempre debe tener en cuenta los valores de ambos operandos.

En Javascript &&y ||tomar un atajo perezoso y no evaluar el segundo operando en ciertos casos y con ello el abandono de sus efectos secundarios. Este comportamiento es imposible de recrear con un xor lógico.


a() && b()evalúa a()y devuelve el resultado si es falso. De lo contrario, evalúa b()y devuelve el resultado. Por lo tanto, el resultado devuelto es verdadero si ambos resultados son verdaderos y falso de lo contrario.

a() || b()evalúa a()y devuelve el resultado si es verdadero. De lo contrario, evalúa b()y devuelve el resultado. Por lo tanto, el resultado devuelto es falso si ambos resultados son falsos y, en caso contrario, verdadero.

Entonces, la idea general es evaluar primero el operando izquierdo. El operando correcto solo se evalúa si es necesario. Y el último valor es el resultado. Este resultado puede ser cualquier cosa. Objetos, números, cadenas ... ¡lo que sea!

Esto hace posible escribir cosas como

image = image || new Image(); // default to a new Image

o

src = image && image.src; // only read out src if we have an image

Pero el valor de verdad de este resultado también se puede utilizar para decidir si un operador lógico "real" habría devuelto verdadero o falso.

Esto hace posible escribir cosas como

if (typeof image.hasAttribute === 'function' && image.hasAttribute('src')) {

o

if (image.hasAttribute('alt') || image.hasAttribute('title')) {

Pero un operador xor "lógico" ( ^^) siempre tendría que evaluar ambos operandos. Esto lo hace diferente a los otros operadores "lógicos" que evalúan el segundo operando solo si es necesario. Creo que es por eso que no hay xor "lógico" en Javascript, para evitar confusiones.


Entonces, ¿qué debería pasar si ambos operandos son falsos? Ambos podrían ser devueltos. Pero solo uno puede ser devuelto. ¿Cúal? ¿El primero? O el segundo? Mi intuición me dice que devuelva los primeros, pero generalmente los operadores "lógicos" evalúan de izquierda a derecha y devuelven el último valor evaluado. ¿O tal vez una matriz que contiene ambos valores?

Y si un operando es verdadero y el otro operando es falso, un xor debería devolver el verdadero. ¿O tal vez una matriz que contiene el verdadero, para que sea compatible con el caso anterior?

Y finalmente, ¿qué debería pasar si ambos operandos son verdaderos? Esperarías algo falso. Pero no hay resultados falsos. Entonces la operación no debería devolver nada. Entonces tal vez undefinedo ... ¿una matriz vacía? Pero una matriz vacía sigue siendo verdadera.

Tomando el enfoque de matriz, terminaría con condiciones como if ((a ^^ b).length !== 1) {. Muy confuso.

Robert
fuente
XOR / ^^ en cualquier idioma siempre tendrá que evaluar ambos operandos, ya que siempre depende de ambos. Lo mismo ocurre con AND / &&, ya que todos los operandos deben ser verdaderos (veraz en JS) como pase de retorno. La excepción es OR / || ya que solo debe evaluar operandos hasta que encuentre un valor verdadero. Si el primer operando en una lista OR es verdadero, ninguno de los otros será evaluado.
Percy
Sin embargo, usted hace un buen punto, que XOR en JS tendría que romper la convención establecida por AND y OR. En realidad, tendría que devolver un valor booleano adecuado en lugar de uno de los dos operandos. Cualquier otra cosa podría causar confusión / complejidad.
Percy
9
@Percy AND / && no evalúa el segundo operando si el primero es falso. Solo evalúa operandos hasta que encuentra un valor falso.
Robert
@DDS Gracias por rectificar la respuesta. Estoy desconcertado de por qué no lo noté yo mismo. Tal vez esto explica la confusión de Percy hasta cierto punto.
Robert
Mi edición fue rechazada, después de lo cual @matts la reeditó exactamente de la manera en que la arreglé, así que perdí mis (miserables) 2 puntos. 3 personas lo rechazaron y estoy desconcertado de lo que usaron como criterio. Thx matts.
DDS
16

El XOR de dos booleanos es simplemente si son diferentes, por lo tanto:

Boolean(a) !== Boolean(b)
DomQ
fuente
12

Convierta valores en forma booleana y luego tome XOR bit a bit. También ayudará con valores no booleanos.

Boolean(a) ^ Boolean(b)
Aman Kaushal
fuente
10

De encubierto a booleano y luego realizar xor como -

!!a ^ !!b
toyeca
fuente
1
Tenga en cuenta que !!a ^ !!bes equivalente a !a ^ !b. Se podrían hacer argumentos sobre cuál es más fácil de leer.
tschwab
9

hay ... una especie de:

if( foo ? !bar : bar ) {
  ...
}

o más fácil de leer:

if( ( foo && !bar ) || ( !foo && bar ) ) {
  ...
}

¿por qué? No sé.

porque los desarrolladores de JavaScript pensaron que sería innecesario ya que puede ser expresado por otros operadores lógicos ya implementados.

también podría tener gon con nand y eso es todo, puede impresionar cualquier otra operación lógica posible a partir de eso.

Personalmente, creo que tiene razones históricas que se basan en lenguajes de sintaxis basados ​​en c, donde, por lo que sé, xor no está presente o al menos es extremadamente raro.

El surrican
fuente
Sí, javascript tiene operaciones ternarias.
mwilcox
Tanto C como Java tienen XOR usando el carácter ^ (caret).
veidelis
7

Sí, solo haz lo siguiente. Suponiendo que se trata de booleanos A y B, el valor de A XOR B se puede calcular en JavaScript utilizando lo siguiente

var xor1 = !(a === b);

La línea anterior también es equivalente a la siguiente

var xor2 = (!a !== !b);

Personalmente, prefiero xor1 ya que tengo que escribir menos caracteres. Creo que xor1 también es más rápido también. Solo está realizando dos cálculos. xor2 está realizando tres cálculos.

Explicación visual ... Lea la tabla a continuación (donde 0 representa falso y 1 representa verdadero) y compare las columnas 3 y 5.

! (A === B):

| A | B | A XOR B | A === B | !(A === B) |
------------------------------------------
| 0 | 0 |    0    |    1    |      0     |
| 0 | 1 |    1    |    0    |      1     |
| 1 | 0 |    1    |    0    |      1     |
| 1 | 1 |    0    |    1    |      0     |
------------------------------------------

Disfrutar.

asiby
fuente
66
var xor1 = !(a === b);es lo mismo quevar xor1 = a !== b;
daniel1426
Esta respuesta no funcionará para todos los tipos de datos (como la respuesta de Premchandra). por ejemplo , !(2 === 3)es true, pero 2y 3son sinceros, así 2 XOR 3debería ser false.
Mariano Desanze
2
Si hubiera leído mi mensaje con más atención, se habría dado cuenta de que escribí "Suponiendo que se trata de booleanos A y B ...".
asiby
5

Revisa:

Puedes imitarlo de esta manera:

if( ( foo && !bar ) || ( !foo && bar ) ) {
  ...
}
Sarfraz
fuente
3
Oye, si añadieran un operador XOR lógico a JavaScript, el ejemplo de código se vería mucho más limpio.
Danyal Aytekin
4

¿Qué tal transformar el resultado int en un bool con doble negación? No tan bonito, pero realmente compacto.

var state1 = false,
    state2 = true;
    
var A = state1 ^ state2;     // will become 1
var B = !!(state1 ^ state2); // will become true
console.log(A);
console.log(B);

Lajos Meszaros
fuente
Esto fallará si los operandos no son booleanos. Una idea mucho mejor esB = ((!state1)!==(!state2))
Doin
Es cierto, pero siempre puedes negar los operandos para lanzarlos, como lo hiciste si no estás seguro de los tipos: B =!!(!state1 ^ !state2); Además, ¿por qué tantos paréntesis? B = !state1 !== !state2; O incluso puede abandonar la negación:B = state1 !== state2;
Lajos Meszaros
Los paréntesis son para mayor claridad, y también para que no tenga que verificar los documentos sobre la precedencia del operador al escribir el código. ;-) Su última expresión sufre de mi queja anterior: falla si los operandos no son booleanos. Pero si está seguro de que lo son, entonces definitivamente es la expresión "xor" lógica más simple y rápida.
Doin
Si con la última expresión quiere decir state1 !== state2, entonces no necesita hacer ninguna conversión allí, ya que !==es un operador lógico, no un bit a bit. 12 !== 4Es cierto 'xy' !== truetambién es cierto. Si usarías en !=lugar de !==, entonces tendrías que hacer casting.
Lajos Meszaros
1
El resultado de ambos !==y !=siempre es booleano ... no estoy seguro de cuál es la distinción que estás haciendo allí, ese no es el problema. El problema es que el operador XOR que queremos es realmente la expresión (Boolean(state1) !== Boolean(state2)). Para booleanos, "xy", 12, 4 y true todos son valores verdaderos, y deben convertirse a true. así ("xy" XOR true)debería ser false, pero en ("xy" !== true)cambio true, como usted señala. Entonces, !==o !=son (ambos) equivalentes a "XOR lógico" si y solo si convierte sus argumentos a booleanos antes de aplicar.
Doin
2

En la función xor anterior, se obtendrá un resultado SIMILAR ya que xor lógico no es exactamente xor lógico, significa que resultará "falso para valores iguales" y "verdadero para valores diferentes" teniendo en cuenta la coincidencia de tipos de datos.

Esta función xor funcionará como xor real u operador lógico , lo que significa que resultará verdadero o falso de acuerdo con los valores de paso verdaderos o falsos . Use de acuerdo a sus necesidades

function xor(x,y){return true==(!!x!==!!y);}

function xnor(x,y){return !xor(x,y);}
Premchandra Singh
fuente
"xnor" es lo mismo que "===".
daniel1426
@ daniel1426 no del todo. Es lo mismo que (!!x) === (!!y). La diferencia es un elenco a booleano. '' === 0es falso, mientras que xnor('', 0)es cierto.
tschwab
2

En mecanografiado (el + cambia a valor numérico):

value : number = (+false ^ +true)

Entonces:

value : boolean = (+false ^ +true) == 1
Witold Kaczurba
fuente
@Sheraff en JavaScript normal, !!(false ^ true)funciona bien con booleanos. En mecanografiado, se requiere + para que sea válido !!(+false ^ +true).
pfg
1

cond1 xor cond2 es equivalente a cond1 + cond 2 == 1 :

Aquí está la prueba:

let ops = [[false, false],[false, true], [true, false], [true, true]];

function xor(cond1, cond2){
  return cond1 + cond2 == 1;
}

for(op of ops){
  console.log(`${op[0]} xor ${op[1]} is ${xor(op[0], op[1])}`)
}

madjaoue
fuente
0

La razón por la que no hay un XOR lógico (^^) es porque, a diferencia de && y || no da ninguna ventaja de lógica perezosa. Ese es el estado de ambas expresiones a la derecha y a la izquierda que deben evaluarse.

usuario7163886
fuente
0

Aquí hay una solución alternativa que funciona con más de 2 variables y proporciona un recuento como bonificación.

Aquí hay una solución más general para simular XOR lógico para cualquier valor verdadero / falso, como si tuviera el operador en declaraciones IF estándar:

const v1 = true;
const v2 = -1; // truthy (warning, as always)
const v3 = ""; // falsy
const v4 = 783; // truthy
const v5 = false;

if( ( !!v1 + !!v2 + !!v3 + !!v4 + !!v5 ) === 1 )
  document.write( `[ ${v1} XOR ${v2} XOR "${v3}" XOR ${v4} XOR ${v5} ] is TRUE!` );
else
  document.write( `[ ${v1} XOR ${v2} XOR "${v3}" XOR ${v4} XOR ${v5} ] is FALSE!` );

La razón por la que me gusta esto es porque también responde "¿Cuántas de estas variables son verdaderas?", Por lo que generalmente guardo previamente ese resultado.

Y para aquellos que desean un estricto comportamiento booleano-VERDADERO o de verificación, simplemente haga:

if( ( ( v1===true ) + ( v2===true ) + ( v3===true ) + ( v4===true ) + ( v5===true ) ) === 1 )
  // etc.

Si no le importa el recuento, o si le importa el rendimiento óptimo: simplemente use el xor bit a bit en los valores obligados a booleano, para la solución verdadero / falso:

if( !!v1 ^ !!v2 ^ !!v3 ^ !!v4 ^ !!v5 )
  // etc.
Ciabaros
fuente
0

Hola, encontré esta solución, para hacer y XOR en JavaScript y TypeScript.

if( +!!a ^ +!!b )
{
  //This happens only when a is true and b is false or a is false and b is true.
}
else
{
  //This happens only when a is true and b is true or a is false and b is false
}
Lucas Solares
fuente
-2

Prueba este breve y fácil de entender

function xor(x,y){return true==(x!==y);}

function xnor(x,y){return !xor(x,y);}

Esto funcionará para cualquier tipo de datos.

Premchandra Singh
fuente
3
Esto no funciona para todos los tipos de datos. Al igual que con un operador de coerción de tipo lógico, esperaría que "foo" xor "bar" sea falso, porque ambos son verdaderos. Ese no es el caso actualmente con su función. En general, true == somebooleanno es necesario hacerlo , así que realmente, lo que has hecho es envolver los estrictos no iguales en una función.
Gijs
Hola GiJs, estoy de acuerdo con su argumento, "foo" y "bar" son valores verdaderos. Pero escribo la función teniendo en cuenta que producirá resultados similares a los de xor (los valores no iguales resultan verdaderos, los valores iguales resultan falsos) no solo para el valor verdadero / falso. Y encontré más uso en tal escenario. Pero estoy escribiendo xor lógico verdadero en otra respuesta a continuación.
Premchandra Singh