Warum gibt es in JavaScript kein logisches xor?

Antworten:

358

JavaScript führt seine Vorfahren auf C zurück, und C hat keinen logischen XOR-Operator. Hauptsächlich, weil es nicht nützlich ist. Bitweises XOR ist äußerst nützlich, aber in all meinen Jahren der Programmierung habe ich nie ein logisches XOR benötigt.

Wenn Sie zwei boolesche Variablen haben, können Sie XOR nachahmen mit:

if (a != b)

Mit zwei beliebigen Variablen können Sie !sie zu booleschen Werten zwingen und dann denselben Trick anwenden:

if (!a != !b)

Das ist allerdings ziemlich dunkel und würde sicherlich einen Kommentar verdienen. In der Tat könnten Sie an dieser Stelle sogar den bitweisen XOR-Operator verwenden, obwohl dies für meinen Geschmack viel zu klug wäre:

if (!a ^ !b)
John Kugelman
quelle
Das einzige Problem dabei !=ist, dass Sie nicht dasselbe tun können wie a ^= b, weil a !== bes sich nur um den strengen Ungleichungsoperator handelt .
Mcpiroman
79

Javascript hat einen bitweisen XOR-Operator: ^

var nb = 5^9 // = 12

Sie können es mit Booleschen Werten verwenden und das Ergebnis wird als 0 oder 1 angegeben (die Sie beispielsweise wieder in Boolesche Werte konvertieren können result = !!(op1 ^ op2)). Aber wie John sagte, ist es gleichbedeutend mit result = (op1 != op2), was klarer ist.

Pikrass
quelle
28
Das ist bitweises XOR, nicht logisches XOR
Ismail Badawi
66
Sie können es als logisches xor verwenden. true^trueist 0 und false^trueist 1.
Pikrass
14
@Pikrass Sie können es als logischen Operator für Boolesche Werte verwenden , jedoch nicht für andere Typen. ||und &&kann als logischer Operator für Nicht-Boolesche Werte verwendet werden (z. B. 5 || 7gibt einen wahrheitsgemäßen Wert zurück, "bob" && nullgibt einen falschen Wert zurück), ^kann dies jedoch nicht. Zum Beispiel ist 5 ^ 72 gleich, was wahr ist.
Mark Amery
10
@ Pikrass Aber leider, (true ^ false) !== truewas es ärgerlich macht mit Bibliotheken, die tatsächliche Boolesche
Werte
2
@Pikrass Sie sollten es niemals als logischen Operator für Boolesche Werte verwenden, da die Implementierung vom Betriebssystem abhängig ist. Ich habe eine Art verwendet, a ^= trueum Boolesche Werte umzuschalten, und es schlägt auf einigen Computern wie Telefonen fehl.
Masadow
30

Es gibt keine echten logischen booleschen Operatoren in Javascript (obwohl dies !ziemlich nahe kommt). Ein logischer Operator würde nur trueoder falseals Operanden nehmen und nur trueoder zurückgebenfalse .

In Javascript &&und|| nimmt alle Arten von Operanden und liefert alle Arten lustiger Ergebnisse (was auch immer Sie in sie zu füttern).

Auch ein logischer Operator sollte immer die Werte beider Operanden berücksichtigen.

In Javascript &&und ||nimmt eine faule Verknüpfung und nicht nicht bewertet die zweiten Operanden in bestimmten Fällen und damit Vernachlässigung ihrer Nebenwirkungen. Dieses Verhalten kann mit einem logischen xor nicht wiederhergestellt werden.


a() && b()wertet a()das Ergebnis aus und gibt es zurück, wenn es falsch ist. Andernfalls wird b()das Ergebnis ausgewertet und zurückgegeben. Daher ist das zurückgegebene Ergebnis wahr, wenn beide Ergebnisse wahr sind, und ansonsten falsch.

a() || b()wertet a()das Ergebnis aus und gibt es zurück, wenn es wahr ist. Ansonsten wird ausgewertetb() das Ergebnis und zurückgegeben. Daher ist das zurückgegebene Ergebnis falsch, wenn beide Ergebnisse falsch sind, und ansonsten wahr.

Die allgemeine Idee ist also, zuerst den linken Operanden auszuwerten. Der richtige Operand wird nur bei Bedarf ausgewertet. Und der letzte Wert ist das Ergebnis. Dieses Ergebnis kann alles sein. Objekte, Zahlen, Strings ... was auch immer!

Dies macht es möglich, Dinge wie zu schreiben

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

oder

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

Der Wahrheitswert dieses Ergebnisses kann aber auch verwendet werden, um zu entscheiden, ob ein "echter" logischer Operator wahr oder falsch zurückgegeben hätte.

Dies macht es möglich, Dinge wie zu schreiben

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

oder

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

Ein "logischer" xor-Operator ( ^^) müsste jedoch immer beide Operanden auswerten. Dies unterscheidet es von den anderen "logischen" Operatoren, die den zweiten Operanden nur bei Bedarf auswerten. Ich denke, aus diesem Grund gibt es in Javascript kein "logisches" xor, um Verwirrung zu vermeiden.


Was soll also passieren, wenn beide Operanden falsch sind? Beide konnten zurückgegeben werden. Es kann jedoch nur einer zurückgegeben werden. Welcher? Der erste? Oder der zweite? Meine Intuition sagt mir, dass ich die ersten, aber normalerweise "logischen" Operatoren von links nach rechts auswerten und den zuletzt ausgewerteten Wert zurückgeben soll. Oder vielleicht ein Array, das beide Werte enthält?

Und wenn ein Operand wahr und der andere falsch ist, sollte ein xor den wahrheitsgemäßen zurückgeben. Oder vielleicht ein Array, das das Wahrhafte enthält, um es mit dem vorherigen Fall kompatibel zu machen?

Und schließlich, was soll passieren, wenn beide Operanden wahr sind? Sie würden etwas Falsches erwarten. Es gibt jedoch keine falschen Ergebnisse. Die Operation sollte also nichts zurückgeben. Also vielleicht undefinedoder ... ein leeres Array? Aber ein leeres Array ist immer noch wahr.

Wenn Sie den Array-Ansatz wählen, erhalten Sie Bedingungen wie if ((a ^^ b).length !== 1) {. Sehr verwirrend.

Robert
quelle
XOR / ^^ in jeder Sprache muss immer beide Operanden auswerten, da es immer von beiden abhängig ist. Gleiches gilt für AND / &&, da alle Operanden wahr sein müssen (wahr in JS). Die Ausnahme ist OR / || da es nur Operanden auswerten muss, bis es einen wahrheitsgemäßen Wert findet. Wenn der erste Operand in einer ODER-Liste wahr ist, wird keiner der anderen ausgewertet.
Percy
Sie machen jedoch einen guten Punkt, dass XOR in JS die durch AND und OR festgelegte Konvention brechen müsste. Es müsste tatsächlich einen richtigen booleschen Wert anstelle eines der beiden Operanden zurückgeben. Alles andere kann Verwirrung / Komplexität verursachen.
Percy
9
@Percy AND / && wertet den zweiten Operanden nicht aus, wenn der erste falsch ist. Es werden nur Operanden ausgewertet, bis ein falscher Wert gefunden wird.
Robert
@DDS Danke, dass du die Antwort korrigiert hast. Ich bin verwirrt, warum ich es selbst nicht bemerkt habe. Vielleicht erklärt dies Percys Verwirrung in gewissem Maße.
Robert
Meine Bearbeitung wurde abgelehnt. Danach hat @matts sie genau so überarbeitet, wie ich sie repariert habe, sodass ich meine (dürftigen) 2 Punkte verpasst habe. 3 Leute haben es abgelehnt und ich bin verblüfft, was sie als ihre Kriterien verwendet haben. Thx Matten.
DDS
16

Das XOR zweier Boolescher Werte ist einfach, ob sie unterschiedlich sind, daher:

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

Konvertieren Sie Werte in eine boolesche Form und nehmen Sie dann bitweises XOR an. Es hilft auch bei nicht-booleschen Werten.

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

Verdeckt auf Boolesch und führe dann xor like - aus

!!a ^ !!b
toyeca
quelle
1
Beachten Sie, dass dies !!a ^ !!bäquivalent zu ist !a ^ !b. Es könnten Argumente vorgebracht werden, die leichter zu lesen sind.
Tschwab
9

es gibt ... irgendwie:

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

oder leichter zu lesen:

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

Warum? Keine Ahnung.

weil Javascript-Entwickler dachten, es wäre unnötig, da es von anderen, bereits implementierten logischen Operatoren ausgedrückt werden kann.

Sie könnten genauso gut mit nand gon haben und das ist es, Sie können jede andere mögliche logische Operation davon beeindrucken.

Ich persönlich denke, es hat historische Gründe, die von c-basierten Syntaxsprachen herrühren, bei denen meines Wissens xor nicht vorhanden oder zumindest äußerst ungewöhnlich ist.

Der Surrican
quelle
Ja, Javascript hat ternäre Operationen.
mwilcox
Sowohl C als auch Java haben XOR mit dem Zeichen ^ (Caret).
Veidelis
7

Ja, machen Sie einfach Folgendes. Angenommen, Sie haben es mit den Booleschen Werten A und B zu tun, kann der Wert A XOR B in JavaScript wie folgt berechnet werden

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

Die vorherige Zeile entspricht auch der folgenden

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

Persönlich bevorzuge ich xor1, da ich weniger Zeichen eingeben muss. Ich glaube, dass xor1 auch schneller ist. Es werden nur zwei Berechnungen durchgeführt. xor2 führt drei Berechnungen durch.

Visuelle Erklärung ... Lesen Sie die folgende Tabelle (wobei 0 für falsch und 1 für wahr steht) und vergleichen Sie die 3. und 5. Spalte.

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

Genießen.

Asiby
quelle
6
var xor1 = !(a === b);ist das gleiche wievar xor1 = a !== b;
daniel1426
Diese Antwort funktioniert nicht für alle Datentypen (wie die Antwort von Premchandra). zB !(2 === 3)ist true, aber 2und 3sind wahr, so 2 XOR 3sollte es sein false.
Mariano Desanze
2
Wenn Sie meine Nachricht genauer gelesen hätten, hätten Sie bemerkt, dass ich "Angenommen, Sie haben es mit den Booleschen Werten A und B zu tun ..." geschrieben habe.
Asiby
5

Auschecken:

Sie können es so etwas nachahmen:

if( ( foo && !bar ) || ( !foo && bar ) ) {
  ...
}
Sarfraz
quelle
3
Hey, wenn sie JavaScript einen logischen XOR-Operator hinzufügen würden, würde das Codebeispiel viel sauberer aussehen.
Danyal Aytekin
4

Wie wäre es, das Ergebnis int in einen Bool mit doppelter Negation umzuwandeln ? Nicht so hübsch, aber sehr kompakt.

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
quelle
Dies schlägt fehl, wenn die Operanden nicht bereits boolesch sind. Viel bessere Idee istB = ((!state1)!==(!state2))
Doin
Stimmt, aber Sie können die Operanden immer negieren, um sie umzuwandeln, wie Sie es getan haben, wenn Sie sich über die Typen nicht sicher sind: B =!!(!state1 ^ !state2); Warum auch so viele Klammern? B = !state1 !== !state2; Oder Sie können sogar die Negation fallen lassen:B = state1 !== state2;
Lajos Meszaros
Klammern dienen der Klarheit, und deshalb muss ich beim Schreiben von Code nicht die Dokumente auf Vorrang des Operators überprüfen! ;-) Dein letzter Ausdruck leidet unter meiner vorherigen Beschwerde: Es schlägt fehl, wenn die Operanden nicht boolesch sind. Aber wenn Sie sicher sind, dass dies der Fall ist, ist dies definitiv der einfachste und schnellste "logische xor" -Ausdruck.
Doin
Wenn Sie mit dem letzten Ausdruck meinen state1 !== state2, müssen Sie dort kein Casting durchführen, da !==es sich um einen logischen Operator handelt, nicht um einen bitweisen. 12 !== 4ist wahr 'xy' !== trueist auch wahr. Wenn Sie !=anstelle von verwenden würden !==, müssten Sie Casting durchführen.
Lajos Meszaros
1
Das Ergebnis von beiden !==und !=ist immer boolesch ... nicht sicher, welche Unterscheidung Sie dort machen sollen, das ist absolut nicht das Problem. Das Problem ist, dass der gewünschte XOR-Operator wirklich der Ausdruck ist (Boolean(state1) !== Boolean(state2)). Für Boolesche Werte sind "xy", 12, 4 und true alle wahrheitsgemäße Werte und sollten in konvertiert werdentrue . so ("xy" XOR true)sollte es sein false, ist es aber ("xy" !== true)stattdessen true, wie Sie betonen. Also !==oder !=sind (beide) gleichbedeutend mit "logischem XOR", wenn und nur wenn Sie ihre Argumente vor dem Anwenden in Boolesche Werte konvertieren .
Doin
2

In der obigen xor-Funktion ergibt sich ein ähnliches Ergebnis, da logisches xor nicht genau logisches xor ist, was bedeutet, dass es "falsch für gleiche Werte" und "wahr für verschiedene Werte" ergibt, wobei die Datentypübereinstimmung berücksichtigt wird.

Diese XOR - Funktion funktioniert als tatsächliche xor oder logischer Operator , Mittel wird es wahr oder falsch gemäß Ergebnis an die Weitergabe Werte sind truthy oder falsy . Verwenden Sie nach Ihren Bedürfnissen

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

function xnor(x,y){return !xor(x,y);}
Premchandra Singh
quelle
"xnor" ist dasselbe wie "===".
Daniel1426
@ daniel1426 nicht ganz. Es ist das gleiche wie (!!x) === (!!y). Der Unterschied ist eine Umwandlung in Boolesche Werte. '' === 0ist falsch, während xnor('', 0)wahr ist.
Tschwab
2

In Typescript (Das + ändert sich in einen numerischen Wert):

value : number = (+false ^ +true)

So:

value : boolean = (+false ^ +true) == 1
Witold Kaczurba
quelle
@Sheraff in normalem Javascript, !!(false ^ true)funktioniert gut mit Booleschen Werten. In Typoskript ist + erforderlich, um es gültig zu machen !!(+false ^ +true).
Pfg
1

cond1 xor cond2 ist äquivalent zu cond1 + cond 2 == 1 :

Hier ist der Beweis:

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
quelle
0

Der Grund, warum es kein logisches XOR (^^) gibt, liegt im Gegensatz zu && und || es gibt keinen faulen logischen Vorteil. Das ist der Zustand beider Ausdrücke rechts und links, die ausgewertet werden müssen.

user7163886
quelle
0

Hier ist eine alternative Lösung, die mit 2+ Variablen arbeitet und als Bonus zählt.

Hier ist eine allgemeinere Lösung, um logisches XOR für alle Wahrheits- / False-Werte zu simulieren, als hätten Sie den Operator in Standard-IF-Anweisungen:

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

Der Grund, warum ich das mag, ist, dass es auch antwortet "Wie viele dieser Variablen sind wahr?", Also speichere ich dieses Ergebnis normalerweise vor.

Und für diejenigen, die ein striktes boolesches-WAHRES xor-Überprüfungsverhalten wünschen, tun Sie einfach:

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

Wenn Sie sich nicht für die Zählung interessieren oder wenn Sie sich für eine optimale Leistung interessieren: Verwenden Sie einfach das bitweise xor für Werte, die zu Booleschen Werten gezwungen werden, um die Lösung für Wahrheit / Falschheit zu erhalten:

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

Hey, ich habe diese Lösung gefunden, um und XOR auf JavaScript und TypeScript zu machen.

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
quelle
-2

Probieren Sie diese kurze und leicht verständliche aus

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

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

Dies funktioniert für jeden Datentyp

Premchandra Singh
quelle
3
Dies funktioniert nicht für alle Datentypen. Wie bei einem Zwangsoperator vom logischen Typ würde ich erwarten, dass "foo" xor "bar" falsch ist, da beide wahr sind. Dies ist derzeit bei Ihrer Funktion nicht der Fall. Im Allgemeinen ist dies true == somebooleannicht erforderlich. Sie haben also die strengen Ungleichheiten in eine Funktion eingewickelt.
Gijs
Hallo GiJs, ich stimme Ihrem Argument zu, "foo" und "bar" sind wahrheitsgemäße Werte. Aber ich schreibe die Funktion unter Berücksichtigung der Tatsache, dass sie eine ähnliche Ausgabe ergibt wie xor (ungleiche Werte ergeben wahr, gleiche Werte ergeben falsch), nicht nur für Wahrheits- / Falschwerte. Und ich fand mehr Verwendung in einem solchen Szenario. Aber ich schreibe wahres logisches xor in eine andere Antwort unten.
Premchandra Singh