Чому в JavaScript немає логічного xor?
Чому в JavaScript немає логічного xor?
Відповіді:
JavaScript простежує своє походження назад до C, а C не має логічного оператора XOR. Головним чином тому, що це не корисно. Бітовий XOR надзвичайно корисний, але за всі мої роки програмування я ніколи не потребував логічного XOR.
Якщо у вас є дві булеві змінні, ви можете імітувати XOR за допомогою:
if (a != b)
За допомогою двох довільних змінних ви можете !
примусити їх до булевих значень, а потім використати той же трюк:
if (!a != !b)
Це досить незрозуміло, і, безумовно, заслуговує на коментар. Дійсно, ви навіть можете використати побітну оператор XOR в цей момент, хоча це було б занадто розумно на мій смак:
if (!a ^ !b)
У Javascript є оператор XOR біт: ^
var nb = 5^9 // = 12
Ви можете використовувати його з булевими, і він дасть результат у вигляді 0 або 1 (який ви можете перетворити назад у булевий, наприклад result = !!(op1 ^ op2)
). Але, як сказав Джон, це рівнозначно result = (op1 != op2)
, що зрозуміліше.
true^true
дорівнює 0, і false^true
є 1.
||
і &&
може бути використаний як логічний оператор на 5 || 7
небулевих (наприклад, повертає просте значення, "bob" && null
повертає значення фальси), але ^
не може. Наприклад, 5 ^ 7
дорівнює 2, що є правдою.
(true ^ false) !== true
це дратує бібліотеки, яким потрібні фактичні
a ^= true
функцію для перемикання булів, і це не вдається на деяких машинах, таких як телефони.
У 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. Це допоможе і з не булевими значеннями.
Boolean(a) ^ Boolean(b)
є ... щось таке:
if( foo ? !bar : bar ) {
...
}
або легше читати:
if( ( foo && !bar ) || ( !foo && bar ) ) {
...
}
чому? данно.
тому що розробники javascript вважали, що це буде зайвим, оскільки це може бути виражено іншими, вже реалізованими логічними операторами.
ви могли б просто мати gon з nand, і це, ви можете справити враження на всі інші можливі логічні операції з цього.
я особисто вважаю, що це має історичні причини, які призводять до мов синтаксису на основі с, де xor немає або, принаймні, надзвичайно рідкісний.
Так, просто виконайте наступне. Якщо припустити, що ви маєте справу з булевими 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 |
------------------------------------------
Насолоджуйтесь.
var xor1 = !(a === b);
те саме, щоvar xor1 = a !== b;
!(2 === 3)
є true
, але 2
і 3
є правдою, так і 2 XOR 3
має бути false
.
Перевіряти:
Ви можете імітувати це приблизно так:
if( ( foo && !bar ) || ( !foo && bar ) ) {
...
}
Як щодо перетворення результату 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))
B =!!(!state1 ^ !state2);
також чому так багато дужок? B = !state1 !== !state2;
Або ви навіть можете відкинути заперечення:B = state1 !== state2;
state1 !== state2
, то вам не потрібно робити жодне кастинг, оскільки !==
це логічний оператор, а не побіжно. 12 !== 4
це правда 'xy' !== true
також правда. Якщо ви використовуєте !=
замість цього !==
, тоді вам доведеться робити кастинг.
!==
і !=
завжди бульний ... не впевнений у тому, якою має бути різниця, яку ви робите там, це абсолютно не проблема. Проблема полягає в тому, що оператор XOR, якого ми хочемо, є насправді виразом (Boolean(state1) !== Boolean(state2))
. Для булевих true
значень "xy", 12, 4, і всі значення truthy, і їх слід перетворити в true
. так ("xy" XOR true)
має бути false
, але ("xy" !== true)
натомість true
, як ви вказуєте. Так !==
або !=
(обидва) еквівалентні "логічному XOR", якщо і тільки якщо ви перетворюєте їх аргументи в булеві, перш ніж застосовувати.
У наведеній вище функції xor це призведе до SIMILAR результату, оскільки логічний xor не є точно логічним xor, значить, це призведе до "false для рівних значень" та "true для різних значень" з урахуванням відповідності типу даних.
Ця функція виключає працюватиме як фактичні виключає або логічний оператор , значить приведуть істинні чи хибні в залежності від проходження значення truthy або falsy . Використовуйте відповідно до своїх потреб
function xor(x,y){return true==(!!x!==!!y);}
function xnor(x,y){return !xor(x,y);}
(!!x) === (!!y)
. Різниця - це відступ до булевих. '' === 0
неправда, а xnor('', 0)
правда.
У Typescript (+ зміни на числове значення):
value : number = (+false ^ +true)
Так:
value : boolean = (+false ^ +true) == 1
!!(false ^ true)
відмінно працює з булевими. У машинописі + потрібно зробити його дійсним !!(+false ^ +true)
.
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])}`)
}
Причина відсутності логічного XOR (^^) полягає в тому, що на відміну від && та || це не дає жодної ліниво-логічної переваги. Такий стан обох виразів праворуч і ліворуч треба оцінювати.
Ось альтернативне рішення, яке працює з 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.
Привіт, я знайшов це рішення, щоб зробити і 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
}
Спробуйте цей короткий і простий для розуміння
function xor(x,y){return true==(x!==y);}
function xnor(x,y){return !xor(x,y);}
Це буде працювати для будь-якого типу даних
true == someboolean
це не обов'язково, так що дійсно, те, що ви зробили, це вкручувати суворі нерівні функції.
!=
полягає в тому, що ви не можете зробити те самеa ^= b
, що, оскількиa !== b
це лише суворий оператор нерівності .