Dlaczego w JavaScript nie ma logicznego xor?

Odpowiedzi:

358

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)
John Kugelman
źródło
Jedyny problem !=polega na tym, że nie można zrobić tego samego a ^= b, ponieważ a !== bjest to tylko operator ścisłej nierówności .
mcpiroman
79

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.

Pikrass
źródło
28
To bitowe XOR, nie logiczne XOR
Ismail Badawi
66
Możesz użyć go jako logicznego xor. true^truewynosi 0, a false^truewynosi 1.
Pikrass
14
@Pikrass Można go używać jako operatora logicznego na boolach , ale nie na innych typach. ||i &&mogą być używane jako operatory logiczne na 5 || 7obiektach niebędących wartościami logicznymi (np. zwracają prawdziwą wartość, "bob" && nullzwracają wartość falsey), ale ^nie mogą. Na przykład 5 ^ 7równa się 2, co jest zgodne z prawdą.
Mark Amery
10
@Pikrass Ale niestety, (true ^ false) !== trueco sprawia, że ​​jest denerwujące w bibliotekach, które wymagają rzeczywistych
boolanów
2
@Pikrass Nigdy nie należy używać go jako operatora logicznego na boolean, ponieważ implementacja zależy od systemu operacyjnego. Używałem pewnego rodzaju a ^= trueprzełączania booleanów, a to nie działa na niektórych komputerach, takich jak telefony.
Masadow
30

W Javascript nie ma prawdziwych logicznych operatorów boolowskich (choć !jest całkiem blisko). Operator logiczny wziąłby tylko truelub falsejako operandy i zwróciłby tylko truelub 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 undefinedlub ... 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.

Robert
źródło
XOR / ^^ w dowolnym języku zawsze będzie musiał oceniać oba operandy, ponieważ zawsze jest zależny od obu. To samo dotyczy AND / &&, ponieważ wszystkie operandy muszą być prawdziwe (zgodnie z JS) pass pass. Wyjątkiem jest OR / || ponieważ musi oceniać operandy, dopóki nie znajdzie prawdziwej wartości. Jeśli pierwszy argument na liście OR jest prawdziwy, żaden z pozostałych nie zostanie oceniony.
Percy
Dajesz jednak rację, że XOR w JS musiałby złamać konwencję określoną przez AND i OR. W rzeczywistości musiałby zwrócić odpowiednią wartość logiczną zamiast jednego z dwóch operandów. Wszystko inne może powodować zamieszanie / złożoność.
Percy
9
@ Percy AND / && nie ocenia drugiego operandu, jeśli pierwszy jest fałszywy. Ocenia tylko operandy, dopóki nie znajdzie wartości fałszowania.
Robert
@DDS Dziękujemy za sprostowanie odpowiedzi. Zastanawiam się, dlaczego sam tego nie zauważyłem. Może to do pewnego stopnia wyjaśnia zamieszanie Percy'ego.
Robert
Moja edycja została odrzucona, po czym @matts dokonało jej ponownej edycji dokładnie tak, jak to naprawiłem, więc straciłem (słabo) 2 punkty. 3 osoby odrzuciły to i jestem zaskoczony tym, co wykorzystali jako swoje kryteria. Dzięki matom.
DDS
16

XOR dwóch booleanów polega na tym, czy są one różne, dlatego:

Boolean(a) !== Boolean(b)
DomQ
źródło
12

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)
Aman Kaushal
źródło
10

Ukryj do wartości logicznej, a następnie wykonaj xor jak -

!!a ^ !!b
toyeca
źródło
1
Zauważ, że !!a ^ !!bjest to równoważne z !a ^ !b. Można argumentować, co jest łatwiejsze do odczytania.
tschwab
9

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.

Surrican
źródło
Tak, javascript ma trójstronne operacje.
mwilcox
Zarówno C, jak i Java mają XOR za pomocą znaku ^ (karetka).
veidelis
7

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ę.

asiby
źródło
6
var xor1 = !(a === b);jest taki sam jakvar xor1 = a !== b;
daniel1426
Ta odpowiedź nie będzie działać dla wszystkich typów danych (takich jak odpowiedź Premchandry). np. !(2 === 3)jest true, ale 2i 3prawdą, tak 2 XOR 3powinno być false.
Mariano Desanze
2
Gdybyś uważniej przeczytał moją wiadomość, zauważyłbyś, że napisałem „Zakładając, że masz do czynienia z booleanami A i B ...”.
asiby
5

Sprawdzić:

Możesz naśladować coś takiego:

if( ( foo && !bar ) || ( !foo && bar ) ) {
  ...
}
Sarfraz
źródło
3
Hej, gdyby dodali logiczny operator XOR do JavaScript, to sprawiłoby, że przykładowy kod wyglądałby znacznie czystiej.
Danyal Aytekin
4

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

Lajos Meszaros
źródło
To się nie powiedzie, jeśli operandy nie są jeszcze boolowskie. Znacznie lepszym pomysłem jestB = ((!state1)!==(!state2))
Doin
To prawda, ale zawsze możesz negować operandy, aby je rzucić, tak jak w przypadku, gdy nie masz pewności co do typów: B =!!(!state1 ^ !state2); Dlaczego więc tak wiele nawiasów? B = !state1 !== !state2; Lub możesz nawet porzucić negację:B = state1 !== state2;
Lajos Meszaros
Nawiasy mają na celu zachowanie przejrzystości, a więc nie muszę sprawdzać dokumentów dotyczących pierwszeństwa operatora podczas pisania kodu! ;-) Twoje ostatnie wyrażenie cierpi z powodu mojej poprzedniej skargi: Nie działa, jeśli operandy nie są logiczne. Ale jeśli jesteś pewien, że tak, to zdecydowanie najprostsze i najszybsze wyrażenie „logiczne xor”.
Doin
Jeśli masz na myśli ostatnie wyrażenie state1 !== state2, nie musisz tam wykonywać rzutowania, ponieważ !==jest operatorem logicznym, nie trochę. 12 !== 4prawda 'xy' !== truejest również prawdą. Jeśli użyjesz !=zamiast tego !==, będziesz musiał wykonać casting.
Lajos Meszaros
1
Wynikiem obu !==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.
Doin
2

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);}
Premchandra Singh
źródło
„xnor” to to samo co „===”.
daniel1426
@ daniel1426 niezupełnie. To jest to samo co (!!x) === (!!y). Różnica polega na obsadzie na boolean. '' === 0jest fałszywe, a xnor('', 0)prawda.
tschwab
2

W maszynopisie (+ zmienia się na wartość liczbową):

value : number = (+false ^ +true)

Więc:

value : boolean = (+false ^ +true) == 1
Witold Kaczurba
źródło
@Sheraff w normalnym javascript, !!(false ^ true)działa dobrze z logami. W maszynopisie wymagane jest +, aby było prawidłowe !!(+false ^ +true).
pfg
1

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

madjaoue
źródło
0

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.

użytkownik7163886
źródło
0

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.
Ciabaros
źródło
0

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
}
Lucas Solares
źródło
-2

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

Premchandra Singh
źródło
3
To nie działa dla wszystkich typów danych. Podobnie jak w przypadku operatora wymuszającego typ logiczny, oczekiwałbym, że „foo” xor „bar” będzie fałszywe, ponieważ oba są prawdziwe. Obecnie nie jest tak w przypadku twojej funkcji. Zasadniczo robienie true == somebooleannie jest konieczne, więc tak naprawdę to, co zrobiłeś, jest zawinięcie ścisłej funkcji nie-równości w funkcję.
Gijs
Cześć GiJ, zgadzam się z twoim argumentem: „foo” i „bar” to prawdziwe wartości. Ale piszę tę funkcję, pamiętając, że da to wynik podobny do xora (nierównomierne wartości to prawda, równe wartości to fałsz), a nie tylko wartość prawda / fałsz. I znalazłem więcej zastosowań w takim scenariuszu. Ale piszę prawdziwy logiczny xor w innej odpowiedzi poniżej.
Premchandra Singh,