Dlaczego w JavaScript nie ma logicznego xor?
Dlaczego w JavaScript nie ma logicznego xor?
Odpowiedzi:
JavaScript śledzi swoje pochodzenie z powrotem do C, a C nie ma logicznego operatora XOR. Głównie dlatego, że to nie jest przydatne. Bitowe XOR jest niezwykle przydatne, ale przez wszystkie lata programowania nigdy nie potrzebowałem logicznego XOR.
Jeśli masz dwie zmienne boolowskie, możesz naśladować XOR za pomocą:
if (a != b)
Za pomocą dwóch dowolnych zmiennych można użyć !
do zmuszenia ich do wartości boolowskich, a następnie użyć tej samej sztuczki:
if (!a != !b)
To dość niejasne i na pewno zasługuje na komentarz. Rzeczywiście, w tym momencie możesz nawet użyć bitowego operatora XOR, choć byłoby to zbyt sprytne jak na mój gust:
if (!a ^ !b)
JavaScript ma bitowy operator XOR: ^
var nb = 5^9 // = 12
Możesz go używać z wartościami logicznymi, a wynik da 0 lub 1 (które możesz przekonwertować z powrotem na wartość logiczną, np result = !!(op1 ^ op2)
.). Ale jak powiedział John, jest to równoważne result = (op1 != op2)
, co jest wyraźniejsze.
true^true
wynosi 0, a false^true
wynosi 1.
||
i &&
mogą być używane jako operatory logiczne na 5 || 7
obiektach niebędących wartościami logicznymi (np. zwracają prawdziwą wartość, "bob" && null
zwracają wartość falsey), ale ^
nie mogą. Na przykład 5 ^ 7
równa się 2, co jest zgodne z prawdą.
(true ^ false) !== true
co sprawia, że jest denerwujące w bibliotekach, które wymagają rzeczywistych
a ^= true
przełączania booleanów, a to nie działa na niektórych komputerach, takich jak telefony.
W Javascript nie ma prawdziwych logicznych operatorów boolowskich (choć !
jest całkiem blisko). Operator logiczny wziąłby tylko true
lub false
jako operandy i zwróciłby tylko true
lub false
.
W Javascript &&
i ||
bierz wszystkie operandy i zwracaj różnego rodzaju zabawne wyniki (cokolwiek do nich dodajesz).
Również operator logiczny powinien zawsze brać pod uwagę wartości obu operandów.
W JavaScript &&
i ||
weź leniwy skrót i nie oceniaj drugiego operandu w niektórych przypadkach, a tym samym zaniedbujesz jego skutki uboczne. To zachowanie jest niemożliwe do odtworzenia przy użyciu logicznego xor.
a() && b()
ocenia a()
i zwraca wynik, jeśli jest to fałsz. W przeciwnym razie ocenia b()
i zwraca wynik. Dlatego zwrócony wynik jest prawdziwy, jeśli oba wyniki są prawdziwe, a fałsz jest inny.
a() || b()
ocenia a()
i zwraca wynik, jeśli jest zgodny z prawdą. W przeciwnym razie oceniab()
i zwraca wynik. Dlatego zwrócony wynik jest fałszem, jeśli oba wyniki są fałszem, a tak naprawdę jest inaczej.
Zatem ogólną ideą jest najpierw ocena lewego operandu. Właściwy operand jest oceniany tylko w razie potrzeby. I ostatnia wartość jest wynikiem. Ten wynik może być dowolny. Obiekty, liczby, ciągi znaków ... cokolwiek!
Umożliwia to pisanie takich rzeczy
image = image || new Image(); // default to a new Image
lub
src = image && image.src; // only read out src if we have an image
Ale wartość prawdy tego wyniku można również wykorzystać do podjęcia decyzji, czy „prawdziwy” operator logiczny zwróciłby wartość prawda czy fałsz.
Umożliwia to pisanie takich rzeczy
if (typeof image.hasAttribute === 'function' && image.hasAttribute('src')) {
lub
if (image.hasAttribute('alt') || image.hasAttribute('title')) {
Ale „logiczny” operator xor ( ^^
) zawsze musiałby oceniać oba operandy. To odróżnia go od innych operatorów „logicznych”, które oceniają drugi argument tylko w razie potrzeby. Myślę, że właśnie dlatego w Javascript nie ma „logicznej” xor, aby uniknąć nieporozumień.
Co więc powinno się stać, jeśli oba operandy są fałszywe? Oba można zwrócić. Ale tylko jeden może zostać zwrócony. Który? Pierwszy? A może drugi? Moja intuicja każe mi zwrócić pierwsze, ale zwykle „logiczne” operatory oceniają od lewej do prawej i zwracają ostatnią ocenianą wartość. A może tablica zawierająca obie wartości?
A jeśli jeden operand jest prawdziwy, a drugi operant jest fałszem, xor powinien zwrócić prawdomówny. A może tablica zawierająca prawdziwą, aby była kompatybilna z poprzednim przypadkiem?
I wreszcie, co powinno się stać, jeśli oba operandy są prawdziwe? Spodziewałbyś się czegoś fałszywego. Ale nie ma wyników fałszywych. Więc operacja nie powinna niczego zwracać. Więc może undefined
lub ... pusta tablica? Ale pusta tablica jest nadal prawdą.
Przyjmując podejście tablicowe, skończyłbyś z takimi warunkami if ((a ^^ b).length !== 1) {
. Bardzo mylące.
Konwertuj wartości na postać logiczną, a następnie weź bitową XOR. Pomoże to również w przypadku wartości innych niż boolowskie.
Boolean(a) ^ Boolean(b)
jest ... rodzaj:
if( foo ? !bar : bar ) {
...
}
lub łatwiejsze do odczytania:
if( ( foo && !bar ) || ( !foo && bar ) ) {
...
}
czemu? Dunno.
ponieważ programiści javascript uważali, że byłoby to niepotrzebne, ponieważ mogą wyrazić to inne, już zaimplementowane, operatory logiczne.
równie dobrze możesz po prostu skończyć z nand i to wszystko, możesz wywrzeć na nim wrażenie z każdej innej logicznej operacji.
osobiście uważam, że ma to historyczne powody, które napędzają języki składniowe oparte na c, gdzie według mojej wiedzy xor nie jest obecny lub przynajmniej wyjątkowo rzadki.
Tak, po prostu wykonaj następujące czynności. Zakładając, że masz do czynienia z booleanami A i B, wówczas wartość A XOR B można obliczyć w JavaScript za pomocą następującego
var xor1 = !(a === b);
Poprzedni wiersz jest również równoważny z poniższym
var xor2 = (!a !== !b);
Osobiście wolę xor1, ponieważ muszę pisać mniej znaków. Wierzę, że xor1 też jest szybszy. Po prostu wykonuje dwa obliczenia. xor2 wykonuje trzy obliczenia.
Objaśnienie wizualne ... Przeczytaj poniższą tabelę (gdzie 0 oznacza fałsz, a 1 oznacza prawda) i porównaj 3 i 5 kolumnę.
! (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 |
------------------------------------------
Cieszyć się.
var xor1 = !(a === b);
jest taki sam jakvar xor1 = a !== b;
!(2 === 3)
jest true
, ale 2
i 3
są prawdą, tak 2 XOR 3
powinno być false
.
Sprawdzić:
Możesz naśladować coś takiego:
if( ( foo && !bar ) || ( !foo && bar ) ) {
...
}
Co powiesz na przekształcenie wyniku int w bool z podwójną negacją? Nie tak ładny, ale naprawdę kompaktowy.
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);
Dlaczego więc tak wiele nawiasów? B = !state1 !== !state2;
Lub możesz nawet porzucić negację:B = state1 !== state2;
state1 !== state2
, nie musisz tam wykonywać rzutowania, ponieważ !==
jest operatorem logicznym, nie trochę. 12 !== 4
prawda 'xy' !== true
jest również prawdą. Jeśli użyjesz !=
zamiast tego !==
, będziesz musiał wykonać casting.
!==
i !=
jest zawsze logiczna ... nie wiem, co rozróżnienie robisz tam ma być, to nie jest absolutnie problem. Problem polega na tym, że operator XOR, którego chcemy, jest naprawdę wyrażeniem (Boolean(state1) !== Boolean(state2))
. W przypadku wartości logicznych „xy”, 12, 4 i true
wszystkie są prawdziwymi wartościami i powinny zostać przekonwertowane na true
. tak ("xy" XOR true)
powinno być false
, ale ("xy" !== true)
jest true
, jak wskazałeś. Tak więc !==
lub !=
są (oba) równoważne z „logicznym XOR” tylko wtedy i tylko wtedy , gdy przekonwertujesz ich argumenty na logiczne przed zastosowaniem.
W powyższej funkcji xor spowoduje PODOBNY wynik, ponieważ xor logiczny nie do końca logiczny xor, oznacza „false dla równych wartości” i „true dla różnych wartości” z typu danych dopasowywania pod uwagę.
Ta funkcja xor będzie działać jako rzeczywisty xor lub operator logiczny , co oznacza, że spowoduje uzyskanie wartości true lub false zgodnie z przekazywanymi wartościami true lub fałsz . Używaj zgodnie z własnymi potrzebami
function xor(x,y){return true==(!!x!==!!y);}
function xnor(x,y){return !xor(x,y);}
(!!x) === (!!y)
. Różnica polega na obsadzie na boolean. '' === 0
jest fałszywe, a xnor('', 0)
prawda.
W maszynopisie (+ zmienia się na wartość liczbową):
value : number = (+false ^ +true)
Więc:
value : boolean = (+false ^ +true) == 1
!!(false ^ true)
działa dobrze z logami. W maszynopisie wymagane jest +, aby było prawidłowe !!(+false ^ +true)
.
cond1 xor cond2
jest równa cond1 + cond 2 == 1
:
Oto dowód:
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])}`)
}
Przyczyną braku logicznego XOR (^^) jest to, że w przeciwieństwie do && i || nie daje żadnej przewagi logicznej. Taki jest stan obu wyrażeń po prawej i lewej stronie.
Oto alternatywne rozwiązanie, które działa z 2+ zmiennymi i zapewnia liczenie jako bonus.
Oto bardziej ogólne rozwiązanie do symulacji logicznego XOR dla dowolnych wartości true / falsey, tak jakbyś miał operatora w standardowych instrukcjach IF:
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!` );
Powodem, dla którego mi się to podoba, jest to, że odpowiada również „Ile z tych zmiennych jest prawdziwych?”, Więc zwykle przechowuję ten wynik wcześniej.
A dla tych, którzy chcą ścisłego zachowania logicznego PRAWDA xor, sprawdź:
if( ( ( v1===true ) + ( v2===true ) + ( v3===true ) + ( v4===true ) + ( v5===true ) ) === 1 )
// etc.
Jeśli nie zależy ci na liczeniu lub zależy Ci na optymalnej wydajności: po prostu użyj bitowej xor na wartościach wymuszonych na wartość logiczną, aby uzyskać rozwiązanie typu prawda / fałsz:
if( !!v1 ^ !!v2 ^ !!v3 ^ !!v4 ^ !!v5 )
// etc.
Hej Znalazłem to rozwiązanie, do zrobienia i XOR w JavaScript i 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
}
Wypróbuj to krótkie i łatwe do zrozumienia
function xor(x,y){return true==(x!==y);}
function xnor(x,y){return !xor(x,y);}
Będzie to działać dla dowolnego typu danych
true == someboolean
nie jest konieczne, więc tak naprawdę to, co zrobiłeś, jest zawinięcie ścisłej funkcji nie-równości w funkcję.
!=
polega na tym, że nie można zrobić tego samegoa ^= b
, ponieważa !== b
jest to tylko operator ścisłej nierówności .