Почему в JavaScript нет логического xor?

Ответы:

358

JavaScript ведет свое происхождение от C, а C не имеет логического оператора XOR. Главным образом потому, что это не полезно. Побитовое XOR чрезвычайно полезно, но за все мои годы программирования мне никогда не требовался логический XOR.

Если у вас есть две логические переменные, вы можете имитировать XOR с помощью:

if (a != b)

С двумя произвольными переменными вы можете использовать !их для приведения к логическим значениям, а затем использовать тот же трюк:

if (!a != !b)

Это довольно неясно, хотя и, безусловно, заслуживает комментария. В самом деле, вы могли бы даже использовать побитовый оператор XOR на этом этапе, хотя это было бы слишком умным на мой вкус:

if (!a ^ !b)
Джон Кугельман
источник
Единственная проблема в !=том, что вы не можете сделать то же самое a ^= b, потому что a !== bэто просто оператор строгого неравенства .
Макпироман
79

В JavaScript есть побитовый оператор XOR: ^

var nb = 5^9 // = 12

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

Pikrass
источник
28
Это битовое XOR, а не логическое XOR
Исмаил Бадави
66
Вы можете использовать его как логический XOR. true^true0 и false^true1
Пикрасс
14
@Pikrass Вы можете использовать его как логический оператор для логических значений , но не для других типов. ||и &&может использоваться в качестве логических операторов для небулевых (например, 5 || 7возвращает истинное значение, "bob" && nullвозвращает ложное значение), но ^не может. Например, 5 ^ 7равно 2, что является правдой.
Марк Эмери
10
@Pikrass Но, к сожалению, (true ^ false) !== trueэто делает его раздражающим в библиотеках, которые требуют фактических логических значений
Izkata
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

Но значение истинности этого результата также можно использовать для определения, вернул ли бы «реальный» логический оператор истину или ложь.

Это позволяет писать такие вещи, как

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

или

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

Но «логический» оператор xor ( ^^) всегда должен оценивать оба операнда. Это отличает его от других «логических» операторов, которые оценивают второй операнд только при необходимости. Я думаю, именно поэтому в Javascript нет «логического» xor, чтобы избежать путаницы.


Так что же должно произойти, если оба операнда ложные? Оба могут быть возвращены. Но только один может быть возвращен. Который из? Первый? Или второй? Моя интуиция подсказывает мне вернуть первые, но обычно «логические» операторы, вычисленные слева направо, и вернуть последнее оцененное значение. Или, может быть, массив, содержащий оба значения?

И если один операнд является правдивым, а другой - ложным, xor должен возвращать правдивый. Или, может быть, массив, содержащий истинный, чтобы сделать его совместимым с предыдущим случаем?

И, наконец, что должно произойти, если оба операнда истинны? Вы ожидаете чего-то ложного. Но нет никаких ложных результатов. Так что операция не должна ничего возвращать. Так может undefinedили .. пустой массив? Но пустой массив все еще правдив.

При использовании массива вы получите такие условия, как if ((a ^^ b).length !== 1) {. Очень запутанно.

Роберт
источник
XOR / ^^ на любом языке всегда должен оценивать оба операнда, поскольку он всегда зависит от обоих. То же самое касается AND / &&, так как все операнды должны быть верны (правда в JS), возвращать проход. Исключением является ИЛИ / || поскольку он должен оценивать только операнды, пока не найдет истинное значение. Если первый операнд в списке ИЛИ верен, ни один из остальных не будет оценен.
Перси
Тем не менее, вы делаете хорошее замечание, что XOR в JS должен был бы нарушить соглашение, установленное AND и OR. На самом деле он должен будет возвращать правильное логическое значение, а не один из двух операндов. Все остальное может вызвать путаницу / сложность.
Перси
9
@Percy AND / && не оценивает второй операнд, если первый опущен. Он только оценивает операнды, пока не найдет ложное значение.
Роберт
@DDS Спасибо за исправление ответа. Я озадачен, почему я сам этого не заметил. Может быть, это объясняет путаницу Перси в некоторой степени.
Роберт
Мое редактирование было отклонено, после чего @matts отредактировал его в точности так, как я его исправил, поэтому я пропустил свои (слегка) 2 пункта. 3 человека отвергли это, и я озадачен тем, что они использовали в качестве своих критериев. Спасибо матс
DDS
16

XOR двух логических выражений заключается просто в том, отличаются ли они, поэтому:

Boolean(a) !== Boolean(b)
DomQ
источник
12

Преобразуйте значения в логическую форму, затем возьмите побитовый XOR. Это поможет и с не булевыми значениями.

Boolean(a) ^ Boolean(b)
Аман Каушал
источник
10

Скрытый для логического, а затем выполнить XOR, как -

!!a ^ !!b
toyeca
источник
1
Обратите внимание, что !!a ^ !!bэквивалентно !a ^ !b. Можно приводить аргументы относительно того, какой из них легче читать.
Чваб
9

есть ... вроде:

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

или проще читать:

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

Зачем? Не знаю.

потому что разработчики javascript думали, что это будет ненужным, поскольку это может быть выражено другими, уже реализованными, логическими операторами.

вы также можете просто получить nand, вот и все, вы можете произвести впечатление на любую другую логическую операцию из этого.

Я лично думаю, что у него есть исторические причины, которые берут начало от языков синтаксиса на основе c, где, насколько мне известно, xor отсутствует или, по крайней мере, крайне необычен.

Суррик
источник
Да, у javascript есть троичные операции.
mwilcox
И C, и Java имеют XOR, используя символ ^ (каретка).
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     |
------------------------------------------

Наслаждаться.

asiby
источник
6
var xor1 = !(a === b);такое же какvar xor1 = a !== b;
daniel1426
Этот ответ не будет работать для всех типов данных (например, ответ Premchandra). например , !(2 === 3)есть true, но 2и 3являются truthy так 2 XOR 3должно быть false.
Мариано Дезанце
2
Если бы вы прочитали мое сообщение более внимательно, вы бы заметили, что я написал «Предполагая, что вы имеете дело с логическими значениями A и B ...».
asiby
5

Проверять, выписываться:

Вы можете имитировать это примерно так:

if( ( foo && !bar ) || ( !foo && bar ) ) {
  ...
}
Sarfraz
источник
3
Эй, если бы они добавили логический оператор XOR в JavaScript, это сделало бы пример кода более понятным.
Данял Айтекин
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;
Лайош Месарос
Скобки для ясности, а также, поэтому мне не нужно проверять документы на приоритет оператора при написании кода! ;-) Ваше последнее выражение страдает от моей предыдущей жалобы: он потерпит неудачу, если операнды не являются логическими. Но если вы уверены, что это так, то это определенно самое простое и быстрое выражение «логический xor».
Doin
Если под последним выражением вы подразумеваете state1 !== state2, тогда вам не нужно выполнять какое-либо приведение, поскольку !==это логический оператор, а не побитовый. 12 !== 4это правда, 'xy' !== trueэто тоже правда. Если бы вы использовали !=вместо !==, то вам придется делать кастинг.
Лайош Месарос
1
Результат обоих !==и !=всегда логичен ... не уверен, какое различие вы там делаете, это не проблема. Проблема в том, что оператор XOR, который мы хотим , действительно является выражением (Boolean(state1) !== Boolean(state2)). Для логических значений «xy», 12, 4 и true все являются истинными значениями, и их следует преобразовать в true. так ("xy" XOR true)должно быть false, но ("xy" !== true)вместо этого true, как вы указываете. Так что !==или !=(оба) эквивалентны «логическому XOR» тогда и только тогда, когда вы преобразуете их аргументы в логические значения перед применением.
Doin
2

В приведенной выше функции XOR это приведет ПОДОБНОМУ результату, так как логический xor не совсем логический xor, означает, что это приведет к «false для равных значений» и «true для разных значений» с учетом соответствия типов данных.

Эта функция xor будет работать как фактический xor или логический оператор , это означает, что она будет иметь значение true или false в соответствии с передаваемыми значениями true или false . Используйте в соответствии с вашими потребностями

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

function xnor(x,y){return !xor(x,y);}
Премчандра Сингх
источник
«xnor» совпадает с «===».
daniel1426
@ daniel1426 не совсем. Это так же, как (!!x) === (!!y). Разница в приведении к логическому. '' === 0ложно, а xnor('', 0)верно.
Чваб
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])}`)
}

madjaoue
источник
0

Причина отсутствия логического XOR (^^) в том, что в отличие от && и || это не дает никакого ленивого логического преимущества. Это состояние обоих выражений справа и слева должны быть оценены.

user7163886
источник
0

Вот альтернативное решение, которое работает с 2+ переменными и обеспечивает счет в качестве бонуса.

Вот более общее решение для имитации логического XOR для любых значений true / 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!` );

Мне это нравится по той причине, что он также отвечает «Сколько из этих переменных являются правдивыми?», Поэтому я обычно предварительно сохраняю этот результат.

А для тех, кто хочет строго проверять булево-TRUE поведение xor, просто выполните:

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

Если вам не важен счет, или если вы заботитесь об оптимальной производительности: просто используйте побитовый xor для значений, приведенных к булевому, для истинного / ложного решения:

if( !!v1 ^ !!v2 ^ !!v3 ^ !!v4 ^ !!v5 )
  // etc.
Ciabaros
источник
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 (неравные значения приводят к истине, равные значения приводят к ложным) не только для значений правда / ложь. И я нашел больше использования в таком сценарии. Но я пишу истинный логический xor в другом ответе ниже.
Премчандра Сингх