Warum gibt es in JavaScript kein logisches xor?
Warum gibt es in JavaScript kein logisches xor?
Antworten:
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)
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.
true^true
ist 0 und false^true
ist 1.
||
und &&
kann als logischer Operator für Nicht-Boolesche Werte verwendet werden (z. B. 5 || 7
gibt einen wahrheitsgemäßen Wert zurück, "bob" && null
gibt einen falschen Wert zurück), ^
kann dies jedoch nicht. Zum Beispiel ist 5 ^ 7
2 gleich, was wahr ist.
(true ^ false) !== true
was es ärgerlich macht mit Bibliotheken, die tatsächliche Boolesche
a ^= true
um Boolesche Werte umzuschalten, und es schlägt auf einigen Computern wie Telefonen fehl.
Es gibt keine echten logischen booleschen Operatoren in Javascript (obwohl dies !
ziemlich nahe kommt). Ein logischer Operator würde nur true
oder false
als Operanden nehmen und nur true
oder 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 undefined
oder ... 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.
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)
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.
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.
var xor1 = !(a === b);
ist das gleiche wievar xor1 = a !== b;
!(2 === 3)
ist true
, aber 2
und 3
sind wahr, so 2 XOR 3
sollte es sein false
.
Auschecken:
Sie können es so etwas nachahmen:
if( ( foo && !bar ) || ( !foo && bar ) ) {
...
}
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);
B = ((!state1)!==(!state2))
B =!!(!state1 ^ !state2);
Warum auch so viele Klammern? B = !state1 !== !state2;
Oder Sie können sogar die Negation fallen lassen:B = state1 !== state2;
state1 !== state2
, müssen Sie dort kein Casting durchführen, da !==
es sich um einen logischen Operator handelt, nicht um einen bitweisen. 12 !== 4
ist wahr 'xy' !== true
ist auch wahr. Wenn Sie !=
anstelle von verwenden würden !==
, müssten Sie Casting durchführen.
!==
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 .
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);}
(!!x) === (!!y)
. Der Unterschied ist eine Umwandlung in Boolesche Werte. '' === 0
ist falsch, während xnor('', 0)
wahr ist.
In Typescript (Das + ändert sich in einen numerischen Wert):
value : number = (+false ^ +true)
So:
value : boolean = (+false ^ +true) == 1
!!(false ^ true)
funktioniert gut mit Booleschen Werten. In Typoskript ist + erforderlich, um es gültig zu machen !!(+false ^ +true)
.
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])}`)
}
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.
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.
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
}
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
true == someboolean
nicht erforderlich. Sie haben also die strengen Ungleichheiten in eine Funktion eingewickelt.
!=
ist, dass Sie nicht dasselbe tun können wiea ^= b
, weila !== b
es sich nur um den strengen Ungleichungsoperator handelt .