Perché non c'è xor logico in JavaScript?


Risposte:


358

JavaScript fa risalire la sua origine a C e C non ha un operatore XOR logico. Principalmente perché non è utile. Bitwise XOR è estremamente utile, ma in tutti i miei anni di programmazione non ho mai avuto bisogno di un XOR logico.

Se hai due variabili booleane puoi simulare XOR con:

if (a != b)

Con due variabili arbitrarie è possibile utilizzare !per forzarli a valori booleani e quindi utilizzare lo stesso trucco:

if (!a != !b)

È piuttosto oscuro e meriterebbe sicuramente un commento. In effetti, potresti persino usare l'operatore XOR bit per bit a questo punto, anche se questo sarebbe troppo intelligente per i miei gusti:

if (!a ^ !b)

L'unico problema !=è che non puoi fare lo stesso a ^= b, perché a !== bè solo l' operatore di disuguaglianza rigorosa .
McComanoman

79

Javascript ha un operatore XOR bit a bit: ^

var nb = 5^9 // = 12

Puoi usarlo con booleani e darà il risultato come 0 o 1 (che puoi riconvertire in booleano, ad es result = !!(op1 ^ op2).). Ma come ha detto John, è equivalente a result = (op1 != op2), che è più chiaro.


28
Questo è XOR bit per bit, non XOR logico
Ismail Badawi il

66
Puoi usarlo come xor logico. true^trueè 0 ed false^trueè 1.
Pikrass il

14
@Pikrass Puoi usarlo come operatore logico su booleani , ma non su altri tipi. ||e &&può essere utilizzato come operatore logico su 5 || 7valori non booleani (ad es. restituisce un valore di verità, "bob" && nullrestituisce un valore di falsa) ma ^non può. Ad esempio, 5 ^ 7uguale a 2, che è vero.
Mark Amery,

10
@Pikrass Ma purtroppo, il (true ^ false) !== trueche lo rende fastidioso con le librerie che richiedono veri e propri booleani
Izkata,

2
@Pikrass Non dovresti mai usarlo come operatore logico su booleano perché l'implementazione dipende dal sistema operativo. Stavo usando una specie di a ^= truetoggle booleani e non funziona su alcune macchine come i telefoni.
Masadow,

30

Non ci sono veri e propri operatori logici booleani in Javascript (sebbene !si avvicini abbastanza). Un operatore logico prenderebbe solo trueo falsecome operandi e ritornerebbe solo trueo false.

In Javascript &&e ||prendere tutti i tipi di operandi e restituire tutti i tipi di risultati divertenti (qualunque cosa si alimenterà di loro).

Inoltre, un operatore logico dovrebbe sempre tenere conto dei valori di entrambi gli operandi.

In Javascript &&e ||prendere una scorciatoia pigri e non valutare il secondo operando in alcuni casi e, quindi, trascurare i suoi effetti collaterali. Questo comportamento è impossibile da ricreare con un xor logico.


a() && b()valuta a()e restituisce il risultato se è falso. Altrimenti valuta b()e restituisce il risultato. Pertanto, il risultato restituito è veritiero se entrambi i risultati sono veritieri e falsa in caso contrario.

a() || b()valuta a()e restituisce il risultato se è vero. Altrimenti valuta b()e restituisce il risultato. Pertanto, il risultato restituito è falso se entrambi i risultati sono falsi e verità in caso contrario.

Quindi l'idea generale è di valutare prima l'operando di sinistra. L'operando giusto viene valutato solo se necessario. E l'ultimo valore è il risultato. Questo risultato può essere qualsiasi cosa. Oggetti, numeri, stringhe .. qualunque cosa!

Questo rende possibile scrivere cose come

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

o

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

Ma il valore di verità di questo risultato può anche essere usato per decidere se un operatore logico "reale" sarebbe tornato vero o falso.

Questo rende possibile scrivere cose come

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

o

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

Ma un operatore xor "logico" ( ^^) dovrebbe sempre valutare entrambi gli operandi. Ciò lo differenzia dagli altri operatori "logici" che valutano il secondo operando solo se necessario. Penso che sia per questo che non c'è xor "logico" in Javascript, per evitare confusione.


Quindi cosa dovrebbe succedere se entrambi gli operandi sono falsi? Entrambi potrebbero essere restituiti. Ma solo uno può essere restituito. Quale? Il primo? O il secondo? La mia intuizione mi dice di restituire i primi ma di solito gli operatori "logici" valutano da sinistra a destra e restituiscono l'ultimo valore valutato. O forse un array contenente entrambi i valori?

E se un operando è veritiero e l'altro operando è falso, un xor dovrebbe restituire quello sincero. O forse un array contenente quello vero, per renderlo compatibile con il caso precedente?

E infine, cosa dovrebbe succedere se entrambi gli operandi sono veri? Ti aspetteresti qualcosa di falso. Ma non ci sono risultati falsi. Quindi l'operazione non dovrebbe restituire nulla. Quindi forse undefinedo ... un array vuoto? Ma un array vuoto è ancora vero.

Adottando l'approccio array dovresti finire con condizioni come if ((a ^^ b).length !== 1) {. Molto confuso.


XOR / ^^ in qualsiasi lingua dovrà sempre valutare entrambi gli operandi poiché dipende sempre da entrambi. Lo stesso vale per AND / &&, poiché tutti gli operandi devono essere pass di ritorno true (true in JS). L'eccezione è OR / || poiché deve solo valutare gli operandi fino a quando non trova un valore veritiero. Se il primo operando in un elenco OR è veritiero, nessuno degli altri verrà valutato.
Percy,

Fai un buon punto, tuttavia, che XOR in JS dovrebbe infrangere la convenzione stabilita da AND e OR. Dovrebbe effettivamente restituire un valore booleano appropriato anziché uno dei due operandi. Qualsiasi altra cosa potrebbe causare confusione / complessità.
Percy,

9
@Percy AND / && non valuta il secondo operando se il primo è falso. Valuta solo gli operandi fino a quando non trova un valore errato.
Robert,

@DDS Grazie per aver corretto la risposta. Sono perplesso sul motivo per cui non me ne sono accorto. Forse questo spiega la confusione di Percy in una certa misura.
Robert,

La mia modifica è stata respinta, dopo di che @matts l'ha modificata esattamente nel modo in cui l'ho riparata, quindi ho perso i miei (miseramente) 2 punti. 3 persone l'hanno rifiutato e sono sconcertato di ciò che hanno usato come criterio. Grazie.
DDS,

16

Lo XOR di due booleani è semplicemente se sono diversi, quindi:

Boolean(a) !== Boolean(b)

12

Converti i valori in forma booleana, quindi prendi XOR bit a bit. Aiuterà anche con valori non booleani.

Boolean(a) ^ Boolean(b)

10

Converti in booleano e quindi esegui xor come -

!!a ^ !!b

1
Si noti che !!a ^ !!bequivale a !a ^ !b. Si potrebbero argomentare su quale sia più facile da leggere.
tschwab,

9

c'è ... sorta di:

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

o più facile da leggere:

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

perché? Boh.

perché gli sviluppatori javascript hanno pensato che non sarebbe necessario in quanto può essere espresso da altri operatori logici, già implementati.

potresti anche avere solo gon con nand e questo è tutto, puoi impressionare ogni altra possibile operazione logica da quello.

personalmente penso che abbia ragioni storiche che derivano da linguaggi di sintassi basati su c, dove per quanto ne so xor non è presente o almeno estremamente raro.


Sì, JavaScript ha operazioni ternarie.
mwilcox,

Sia C che Java hanno XOR usando il carattere ^ (punto di inserimento).
Veidelis,

7

Sì, basta fare quanto segue. Supponendo che tu abbia a che fare con i booleani A e B, il valore A XOR B può essere calcolato in JavaScript usando il seguente

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

La riga precedente equivale anche alla seguente

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

Personalmente, preferisco xor1 poiché devo digitare meno caratteri. Credo che anche xor1 sia anche più veloce. Sta solo eseguendo due calcoli. xor2 sta eseguendo tre calcoli.

Spiegazione visiva ... Leggi la tabella qui sotto (dove 0 sta per falso e 1 sta per vero) e confronta la 3a e la 5a colonna.

! (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     |
------------------------------------------

Godere.


6
var xor1 = !(a === b);è lo stesso divar xor1 = a !== b;
daniel1426,

Questa risposta non funzionerà per tutti i tipi di dati (come la risposta di Premchandra). es !(2 === 3)è true, ma 2e 3sono truthy così 2 XOR 3dovrebbe essere false.
Mariano Desanze,

2
Se avessi letto il mio messaggio più attentamente, avresti notato che ho scritto "Supponendo che tu abbia a che fare con i booleani A e B ...".
asiby,


4

Che ne dite di trasformare il risultato int a un bool con doppia negazione? Non così carino, ma davvero compatto.

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


Questo fallirà se gli operandi non sono già booleani. L'idea molto migliore èB = ((!state1)!==(!state2))
Doin il

È vero, ma puoi sempre negare agli operandi di lanciarli, come hai fatto se non sei sicuro dei tipi: B =!!(!state1 ^ !state2); Inoltre, perché così tante parentesi? B = !state1 !== !state2; Oppure puoi persino abbandonare la negazione:B = state1 !== state2;
Lajos Meszaros il

Le parentesi sono per chiarezza e quindi non devo controllare i documenti sulla precedenza dell'operatore durante la scrittura del codice! ;-) La tua ultima espressione soffre della mia precedente lamentela: fallisce se gli operandi non sono booleani. Ma se sei sicuro che lo siano, allora è sicuramente l'espressione "xor" logica più semplice e veloce.
Doin,

Se per ultima espressione intendi state1 !== state2, allora non è necessario eseguire alcun casting lì, poiché !==è un operatore logico, non bit a bit. 12 !== 4è vero 'xy' !== trueè anche vero. Se dovessi usare !=invece di !==, allora dovresti fare il casting.
Lajos Meszaros,

1
Il risultato di entrambi !==ed !=è sempre booleano ... non sono sicuro di quale sia la distinzione che stai facendo, non è assolutamente il problema. Il problema è che l'operatore XOR che vogliamo è davvero l'espressione (Boolean(state1) !== Boolean(state2)). Per i booleani, "xy", 12, 4 e true sono tutti valori di verità, e dovrebbero convertirsi in true. così ("xy" XOR true)dovrebbe essere false, ma ("xy" !== true)è invece true, come fai notare. Quindi !==o !=sono (entrambi) equivalenti a "XOR logico" se e solo se convertite i loro argomenti in booleani prima dell'applicazione.
Doin,

2

Nella precedente funzione xor si otterrà un risultato SIMILE in quanto xor logico non esattamente xor logico, significa che risulterà "falso per valori uguali" e "vero per valori diversi" tenendo conto della corrispondenza del tipo di dati.

Questa funzione xor funzionerà come vero xor o operatore logico , significa che risulterà vero o falso secondo che i valori di passaggio sono veritieri o falsi . Utilizzare in base alle proprie esigenze

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

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

"xnor" è uguale a "===".
daniel1426,

@ daniel1426 non del tutto. È lo stesso di (!!x) === (!!y). La differenza è un cast booleano. '' === 0è falso, mentre xnor('', 0)è vero.
tschwab,

2

In Typescript (Il + cambia in valore numerico):

value : number = (+false ^ +true)

Così:

value : boolean = (+false ^ +true) == 1

@Sheraff in javascript normale, !!(false ^ true)funziona bene con i booleani. In dattiloscritto, + è necessario per renderlo valido !!(+false ^ +true).
pag

1

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

Ecco la prova:

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])}`)
}


0

Il motivo per cui non esiste un XOR logico (^^) è perché a differenza di && e || non offre alcun vantaggio logico. Questo è lo stato di entrambe le espressioni a destra e a sinistra che devono essere valutate.


0

Ecco una soluzione alternativa che funziona con 2+ variabili e fornisce il conteggio come bonus.

Ecco una soluzione più generale per simulare XOR logico per qualsiasi valore di verità / falsità, proprio come se avessi l'operatore in istruzioni IF standard:

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

Il motivo per cui mi piace questo è perché risponde anche "Quante di queste variabili sono veritiere?", Quindi di solito pre-memorizzo quel risultato.

E per coloro che desiderano un comportamento booleano-TRUE xor check rigoroso, basta fare:

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

Se non ti interessa il conteggio o se ti preoccupi delle prestazioni ottimali: usa semplicemente lo xor bit a bit sui valori forzati a booleani, per la soluzione verità / falsa:

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

0

Ehi, ho trovato questa soluzione, per creare e XOR su JavaScript e 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
}

-2

Prova questo breve e facile da capire

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

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

Funzionerà con qualsiasi tipo di dati


3
Questo non funziona per tutti i tipi di dati. Come con un operatore di tipo logico, mi aspetto che "foo" xor "bar" sia falso, perché entrambi sono veritieri. Questo non è attualmente il caso con la tua funzione. Generalmente, fare true == somebooleannon è necessario, quindi in realtà ciò che hai fatto è avvolgere i rigidi non uguali in una funzione.
Gijs,

Ciao GiJs, concordo sul fatto che "foo" e "bar" sono valori veritieri. Ma scrivo la funzione tenendo presente che risulterà un output simile a quello di xor (valori disuguali risultano veri, valori uguali risultati falsi) non solo per valore verità / falsa. E ho trovato più utilizzo in tale scenario. Ma sto scrivendo il vero xor logico in un'altra risposta di seguito.
Premchandra Singh,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.