Чому в JavaScript немає логічного xor?


Відповіді:


358

JavaScript простежує своє походження назад до C, а C не має логічного оператора XOR. Головним чином тому, що це не корисно. Бітовий XOR надзвичайно корисний, але за всі мої роки програмування я ніколи не потребував логічного XOR.

Якщо у вас є дві булеві змінні, ви можете імітувати XOR за допомогою:

if (a != b)

За допомогою двох довільних змінних ви можете !примусити їх до булевих значень, а потім використати той же трюк:

if (!a != !b)

Це досить незрозуміло, і, безумовно, заслуговує на коментар. Дійсно, ви навіть можете використати побітну оператор XOR в цей момент, хоча це було б занадто розумно на мій смак:

if (!a ^ !b)

Єдина проблема !=полягає в тому, що ви не можете зробити те саме a ^= b, що, оскільки a !== bце лише суворий оператор нерівності .
mcpiroman

79

У Javascript є оператор XOR біт: ^

var nb = 5^9 // = 12

Ви можете використовувати його з булевими, і він дасть результат у вигляді 0 або 1 (який ви можете перетворити назад у булевий, наприклад result = !!(op1 ^ op2)). Але, як сказав Джон, це рівнозначно result = (op1 != op2), що зрозуміліше.


28
Це побіжно XOR, не логічно XOR
Ісмаїл Бадаві

66
Ви можете використовувати його як логічний xor. true^trueдорівнює 0, і false^trueє 1.
Пікрасс

14
@Pikrass Ви можете використовувати його як логічний оператор на булевих , але не на інших типах. ||і &&може бути використаний як логічний оператор на 5 || 7небулевих (наприклад, повертає просте значення, "bob" && nullповертає значення фальси), але ^не може. Наприклад, 5 ^ 7дорівнює 2, що є правдою.
Марк Амері

10
@Pikrass Але, на жаль, (true ^ false) !== trueце дратує бібліотеки, яким потрібні фактичні
булеви

2
@Pikrass Ви ніколи не повинні використовувати його як логічний оператор на булевому рівні, оскільки реалізація залежить від ОС. Я використовував якусь a ^= trueфункцію для перемикання булів, і це не вдається на деяких машинах, таких як телефони.
Масадов

30

У Javascript немає справжніх логічних булевих операторів (хоча він !досить близький). Логічний оператор , тільки взяти б trueабо falseяк операнди і буде повертати тільки trueчи false.

У Javascript &&і ||прийміть всі види операндів і повертайте всілякі смішні результати (що б ви не додавали до них).

Також логічний оператор повинен завжди враховувати значення обох операндів.

У Javascript &&і ||візьміть ледачий ярлик і не оцінюйте другий операнд у певних випадках і тим самим нехтуйте його побічними ефектами. Таку поведінку неможливо відтворити логічним xor.


a() && b()оцінює a()та повертає результат, якщо він хибний. В іншому випадку вона оцінює b()і повертає результат. Таким чином, повернутий результат є правдоподібним, якщо обидва результати є неправдивими, а помилковими - інакше.

a() || b()оцінює a()та повертає результат, якщо він правдоподібний. В іншому випадку вона оцінює b()і повертає результат. Тому повернутий результат є хибним, якщо обидва результати хибні, а інакші - інакше.

Тож загальною ідеєю є спочатку оцінити лівий операнд. Правий операнд оцінюється лише за потреби. І останнє значення - результат. Цей результат може бути будь-яким. Об'єкти, числа, рядки .. що завгодно!

Це дає можливість писати такі речі

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

або

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

Але значення істинності цього результату також може бути використане для вирішення, чи справжній логічний оператор повернув би значення true та false.

Це дає можливість писати такі речі

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

або

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

Але "логічному" оператору xor ( ^^) завжди доведеться оцінювати обидва операнди. Це відрізняє від інших "логічних" операторів, які оцінюють другий операнд лише у разі потреби. Я думаю, саме тому в Javascript немає "логічного" xor, щоб уникнути плутанини.


Отже, що має статися, якщо обидва операнди хибні? Обидва можна було повернути. Але повернути лише один. Який? Перший? Або другий? Моя інтуїція підказує мені повернути перше, але зазвичай "логічне", оператори оцінюють зліва направо і повертають останнє оцінене значення. А може бути масив, що містить обидва значення?

І якщо один операнд є правдоподібним, а інший операндом - хибним, xor повинен повернути його. Або, можливо, масив, що містить простий, щоб зробити його сумісним з попереднім випадком?

І нарешті, що повинно статися, якщо обидва операнди є truthy? Ви очікували б чогось хибного. Але хибних результатів немає. Тож операція нічого не повинна повертати. То, може, undefinedчи .. порожній масив? Але порожній масив все ще є правдою.

Якщо взяти підхід до масиву, у вас виникнуть такі умови if ((a ^^ b).length !== 1) {. Дуже заплутано.


XOR / ^^ на будь-якій мові завжди доведеться оцінювати обидва операнди, оскільки це завжди залежить від обох. Те ж саме стосується AND / &&, оскільки всі операнди повинні бути істинними (truthy в JS) return pass. Виняток - АБО / || оскільки він повинен оцінювати операнди лише до тих пір, поки не знайде істинне значення. Якщо перший операнд у списку АБО є правдоподібним, жоден з інших не буде оцінений.
Персі

Ви все-таки гарно зазначаєте, що XOR в JS повинен був би порушити конвенцію, встановлену AND і OR. Фактично, він повинен був би повернути належне булеве значення, а не один з двох операндів. Все інше може викликати плутанину / складність.
Персі

9
@Percy AND / && не оцінює другий операнд, якщо перший хибний. Він оцінює лише операнди, поки не знайде хибне значення.
Роберт

@DDS Дякуємо за виправлення відповіді. Мені спантеличено, чому я сам цього не помітив. Можливо, це певною мірою пояснює плутанину Персі.
Роберт

Мою редакцію було відхилено, після чого @matts змінив її саме так, як я її виправив, тому я пропустив свої (легкі) 2 бали. 3 люди відхилили це, і я здивований тим, що вони використовували як свої критерії. Thx матові.
DDS

16

XOR двох булевих просто тому, чи відрізняються вони, отже:

Boolean(a) !== Boolean(b)

12

Перетворять значення в булеву форму, а потім приймають побіжно XOR. Це допоможе і з не булевими значеннями.

Boolean(a) ^ Boolean(b)

10

Приховати до булевого, а потім виконати xor як -

!!a ^ !!b

1
Зауважте, що !!a ^ !!bеквівалентно !a ^ !b. Можна стверджувати, що простіше читати.
tschwab

9

є ... щось таке:

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

або легше читати:

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

чому? данно.

тому що розробники javascript вважали, що це буде зайвим, оскільки це може бути виражено іншими, вже реалізованими логічними операторами.

ви могли б просто мати gon з nand, і це, ви можете справити враження на всі інші можливі логічні операції з цього.

я особисто вважаю, що це має історичні причини, які призводять до мов синтаксису на основі с, де xor немає або, принаймні, надзвичайно рідкісний.


Так, у javascript є потрійні опції.
mwilcox

І C, і Java мають XOR, використовуючи символ ^ (caret).
veidelis

7

Так, просто виконайте наступне. Якщо припустити, що ви маєте справу з булевими A і B, то значення XOR B можна обчислити в JavaScript, використовуючи наступне

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

Попередній рядок також еквівалентний наступному

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

Особисто я віддаю перевагу xor1, оскільки мені доводиться набирати менше символів. Я вважаю, що xor1 теж швидший. Це просто виконання двох обчислень. xor2 виконує три обчислення.

Візуальне пояснення ... Прочитайте таблицю нижче (де 0 означає помилкове та 1 - істинне) та порівняйте 3-й та 5-й стовпці.

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

Насолоджуйтесь.


6
var xor1 = !(a === b);те саме, щоvar xor1 = a !== b;
daniel1426

Ця відповідь не буде працювати для всіх типів даних (наприклад, відповідь Премчандри). наприклад !(2 === 3)є true, але 2і 3є правдою, так і 2 XOR 3має бути false.
Mariano Desanze

2
Якби ви уважніше прочитали моє повідомлення, ви помітили б, що я написав "Припускаючи, що ви маєте справу з булевими A і B ...".
asiby


4

Як щодо перетворення результату int в bool з подвійним запереченням? Не такий гарний, але справді компактний.

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

Щоправда, але ви завжди можете заперечувати операнди, щоб їх передати, як ви це робили, якщо ви не впевнені у типах: B =!!(!state1 ^ !state2); також чому так багато дужок? B = !state1 !== !state2; Або ви навіть можете відкинути заперечення:B = state1 !== state2;
Lajos Meszaros

Парентези призначені для ясності, а також тому мені не потрібно перевіряти документи на пріоритетність оператора під час написання коду! ;-) Ваше останнє вираження страждає від моєї попередньої скарги: не вдається, якщо операнди не булеві. Але якщо ви впевнені, що вони є, то це, безумовно, найпростіший і найшвидший вираз "логічного xor".
Зробіть

Якщо під останнім виразом ви маєте на увазі state1 !== state2, то вам не потрібно робити жодне кастинг, оскільки !==це логічний оператор, а не побіжно. 12 !== 4це правда 'xy' !== trueтакож правда. Якщо ви використовуєте !=замість цього !==, тоді вам доведеться робити кастинг.
Лайош Мессарос

1
Результат обох !==і !=завжди бульний ... не впевнений у тому, якою має бути різниця, яку ви робите там, це абсолютно не проблема. Проблема полягає в тому, що оператор XOR, якого ми хочемо, є насправді виразом (Boolean(state1) !== Boolean(state2)). Для булевих true значень "xy", 12, 4, і всі значення truthy, і їх слід перетворити в true. так ("xy" XOR true)має бути false, але ("xy" !== true)натомість true, як ви вказуєте. Так !==або !=(обидва) еквівалентні "логічному XOR", якщо і тільки якщо ви перетворюєте їх аргументи в булеві, перш ніж застосовувати.
Зробіть

2

У наведеній вище функції xor це призведе до SIMILAR результату, оскільки логічний xor не є точно логічним xor, значить, це призведе до "false для рівних значень" та "true для різних значень" з урахуванням відповідності типу даних.

Ця функція виключає працюватиме як фактичні виключає або логічний оператор , значить приведуть істинні чи хибні в залежності від проходження значення truthy або falsy . Використовуйте відповідно до своїх потреб

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

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

"xnor" - це те саме, що "===".
daniel1426

@ daniel1426 не зовсім. Це те саме, що (!!x) === (!!y). Різниця - це відступ до булевих. '' === 0неправда, а xnor('', 0)правда.
tschwab

2

У Typescript (+ зміни на числове значення):

value : number = (+false ^ +true)

Так:

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

@Sheraff у звичайному javascript, !!(false ^ true)відмінно працює з булевими. У машинописі + потрібно зробити його дійсним !!(+false ^ +true).
pfg

1

cond1 xor cond2еквівалентно cond1 + cond 2 == 1:

Ось доказ:

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

Причина відсутності логічного XOR (^^) полягає в тому, що на відміну від && та || це не дає жодної ліниво-логічної переваги. Такий стан обох виразів праворуч і ліворуч треба оцінювати.


0

Ось альтернативне рішення, яке працює з 2+ змінними і забезпечує рахунок як бонус.

Ось більш загальне рішення для моделювання логічного XOR для будь-яких значень truthy / falsey, так само, як якщо б у вас був оператор у стандартних операторах 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!` );

Причина, що мені це подобається, полягає в тому, що вона також відповідає "Скільки цих змінних є правдоподібними?", Тому я зазвичай попередньо зберігаю цей результат.

А для тих, хто бажає суворої булевої-ПРАВИЛЬНОЇ поведінки xor check, просто зробіть:

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

Якщо вам не байдуже чисельність, або якщо ви піклуєтесь про оптимальну продуктивність: просто використовуйте побітове значення xor на значення, примусові до булевих, для рішення truthy / falesy:

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

0

Привіт, я знайшов це рішення, щоб зробити і XOR на JavaScript і 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

Спробуйте цей короткий і простий для розуміння

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

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

Це буде працювати для будь-якого типу даних


3
Це працює не для всіх типів даних. Як і у випадку з оператором примусового логічного типу, я б очікував, що "foo" xor "bar" буде помилковим, тому що обидва є правдоподібними. Наразі це не стосується вашої функції. Як правило, робити true == somebooleanце не обов'язково, так що дійсно, те, що ви зробили, це вкручувати суворі нерівні функції.
Gijs

Привіт GiJs, я погоджуюся з вашим аргументом, "foo" і "bar" - це правдиві значення. Але я пишу цю функцію, маючи на увазі, що це призведе до аналогічного виходу, як і xor (нерівні значення результатів є істинними, однакові значення результатів помилкові), а не лише для значення truthy / false. І я знайшов більше використання в такому сценарії. Але я пишу справжній логічний xor в іншій відповіді нижче.
Премчандра Сінгх
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.