เหตุใดจึงไม่มีตรรกะ xor ใน JavaScript


คำตอบ:


358

JavaScript ติดตามบรรพบุรุษของมันกลับไปที่ C และ C ไม่มีตัวดำเนินการ XOR แบบลอจิคัล ส่วนใหญ่เป็นเพราะมันไม่มีประโยชน์ Bitwise XOR มีประโยชน์อย่างยิ่ง แต่ในทุกปีของการเขียนโปรแกรมฉันไม่เคยต้องการ XOR เชิงตรรกะ

หากคุณมีตัวแปรบูลีนสองตัวคุณสามารถเลียนแบบ XOR ด้วย:

if (a != b)

ด้วยตัวแปรตามอำเภอใจสองตัวที่คุณสามารถใช้!เพื่อบีบบังคับค่าบูลีนแล้วใช้กลอุบายแบบเดียวกัน:

if (!a != !b)

ซึ่งค่อนข้างคลุมเครือและแน่นอนว่าควรได้รับความคิดเห็น แน่นอนคุณสามารถใช้ตัวดำเนินการ XOR bitwise ณ จุดนี้แม้ว่ามันจะฉลาดเกินไปสำหรับรสนิยมของฉัน:

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
นั่นคือ bitor XOR ไม่ใช่ XOR แบบตรรกะ
Ismail Badawi

66
คุณสามารถใช้มันเป็นตรรกะ xor true^trueคือ 0 และfalse^trueคือ 1
Pikrass

14
@Pikrass คุณสามารถใช้มันเป็นตัวดำเนินการเชิงตรรกะบน booleansแต่ไม่ใช่ประเภทอื่น ||และ&&สามารถใช้เป็นตัวดำเนินการเชิงตรรกะบน non-booleans (เช่น5 || 7ส่งคืนค่าความจริง"bob" && nullส่งกลับค่าเท็จ) แต่^ไม่สามารถทำได้ ตัวอย่างเช่น5 ^ 7เท่ากับ 2 ซึ่งเป็นความจริง
Mark Amery

10
@Pikrass แต่น่าเสียดาย(true ^ false) !== trueที่ทำให้มันน่ารำคาญกับห้องสมุดที่ต้องใช้บูลีนจริง ๆ
Izkata

2
@Pikrass คุณไม่ควรใช้มันเป็นโอเปอเรเตอร์เชิงตรรกะสำหรับบูลีนเนื่องจากการติดตั้งนั้นขึ้นอยู่กับระบบปฏิบัติการ ฉันใช้a ^= trueสลับเป็น booleans และมันล้มเหลวในเครื่องบางอย่างเช่นโทรศัพท์
Masadow

30

ไม่มีตัวดำเนินการบูลีนตรรกะจริงใน Javascript (แม้ว่าจะ!ใกล้เคียงกันมาก) ผู้ประกอบการตรรกะจะใช้เวลาเพียงtrueหรือfalseเป็นตัวถูกดำเนินการและจะกลับมาหรือtruefalse

ใน 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 ( ^^) จะต้องประเมินตัวถูกดำเนินการทั้งสองเสมอ สิ่งนี้ทำให้แตกต่างจากตัวดำเนินการ "ตรรกะ" อื่น ๆ ซึ่งประเมินตัวถูกดำเนินการตัวที่สองเฉพาะในกรณีที่จำเป็นเท่านั้น ฉันคิดว่านี่เป็นสาเหตุที่ไม่มี "ตรรกะ" xor ใน Javascript เพื่อหลีกเลี่ยงความสับสน


แล้วจะเกิดอะไรขึ้นถ้าตัวถูกดำเนินการทั้งสองผิดพลาด? ทั้งสองสามารถส่งคืนได้ แต่สามารถคืนได้เพียงรายการเดียวเท่านั้น อันไหน? คนแรก? หรืออันที่สอง? ปรีชาญาณของฉันบอกให้ฉันกลับผู้ดำเนินการรายแรก แต่มักจะประเมินตรรกะจากซ้ายไปขวาและคืนค่าที่ประเมินล่าสุด หรืออาจเป็นอาร์เรย์ที่มีค่าทั้งสอง?

และถ้าตัวถูกดำเนินการตัวใดตัวหนึ่งเป็นความจริงและตัวถูกดำเนินการตัวหนึ่งนั้นเป็นคนโกหก xor ควรกลับคืนความจริง หรืออาจเป็นอาเรย์ที่บรรจุสัจธรรมเพื่อให้เข้ากันได้กับเคสก่อนหน้า?

และในที่สุดสิ่งที่จะเกิดขึ้นถ้าตัวถูกดำเนินการทั้งสองเป็นจริง คุณคาดหวังบางสิ่งบางอย่างผิดพลาด แต่ไม่มีผลลัพธ์ที่เป็นเท็จ ดังนั้นการดำเนินการไม่ควรส่งคืนสิ่งใด ดังนั้นอาจundefinedหรือ .. อาร์เรย์ว่างเปล่า? แต่อาเรย์ที่ว่างยังคงเป็นความจริง

if ((a ^^ b).length !== 1) {การใช้วิธีอาร์เรย์ที่คุณจะจบลงด้วยสภาพเช่น สับสนมาก


XOR / ^^ ในภาษาใด ๆ จะต้องประเมินตัวถูกดำเนินการทั้งสองเสมอเพราะมันขึ้นอยู่กับทั้งสองเสมอ เช่นเดียวกับ AND / && เนื่องจากตัวถูกดำเนินการทั้งหมดจะต้องเป็นจริง (ความจริงใน JS) ส่งคืนรหัสผ่าน ข้อยกเว้นคือ OR / || เพราะมันจะต้องประเมินตัวถูกดำเนินการจนกว่าจะพบค่าความจริง หากตัวถูกดำเนินการแรกในรายการ OR เป็นความจริงจะไม่มีการประเมินผู้อื่น
Percy

คุณทำได้ดีแม้ว่า XOR ใน JS จะต้องทำลายการประชุมที่กำหนดโดย AND และ OR มันจะต้องส่งคืนค่าบูลีนที่เหมาะสมมากกว่าหนึ่งในสองตัวถูกดำเนินการ อะไรก็ตามที่อาจทำให้เกิดความสับสน / ความซับซ้อน
Percy

9
@Percy AND / && ไม่ได้ประเมินตัวถูกดำเนินการที่สองหากรายการแรกเป็นเท็จ มันจะประเมินค่าตัวถูกดำเนินการจนกว่าจะพบค่าที่เป็นเท็จ
Robert

@DDS ขอบคุณสำหรับการแก้ไขคำตอบ ฉันงงว่าทำไมฉันถึงไม่สังเกตตัวเอง บางทีนี่อาจอธิบายความสับสนของเพอร์ซี่ได้บ้าง
Robert

การแก้ไขของฉันถูกปฏิเสธหลังจากที่ @matts ทำการแก้ไขอีกครั้งตามวิธีที่ฉันแก้ไขดังนั้นฉันจึงพลาด 2 คะแนน (เลวร้ายที่สุด) 3 คนปฏิเสธมันและฉันงงกับสิ่งที่พวกเขาใช้เป็นเกณฑ์ของพวกเขา ขอบคุณ matts
DDS

16

XOR ของสองบูลีนเป็นเพียงว่าพวกเขาแตกต่างกันดังนั้น:

Boolean(a) !== Boolean(b)

12

แปลงค่าเป็นรูปแบบบูลีนจากนั้นใช้ค่า XOR มันจะช่วยให้มีค่าที่ไม่ใช่แบบบูลเช่นกัน

Boolean(a) ^ Boolean(b)

10

แปลงเป็นบูลีนแล้วทำ xor like -

!!a ^ !!b

1
โปรดทราบว่าเทียบเท่ากับ!!a ^ !!b !a ^ !bอาร์กิวเมนต์สามารถอ่านได้ง่ายกว่า
tschwab

9

มี ... ประเภทของ:

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

หรือง่ายต่อการอ่าน:

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

ทำไม? dunno

เพราะผู้พัฒนาจาวาสคริปต์คิดว่ามันจะไม่จำเป็นเพราะมันสามารถแสดงออกได้โดยผู้ประกอบการอื่น ๆ ที่ดำเนินการแล้วตรรกะ

คุณอาจจะมีแค่ gon กับ nand และคุณสามารถสร้างความประทับใจให้กับการดำเนินการทางตรรกะอื่น ๆ

โดยส่วนตัวฉันคิดว่ามันมีเหตุผลทางประวัติศาสตร์ที่ขับเคลื่อนจากภาษาไวยากรณ์แบบอิง c ซึ่งความรู้ของฉันไม่มีอยู่หรืออย่างน้อยก็ผิดปกติ


ใช่จาวาสคริปต์มี opernary
mwilcox

ทั้ง C และ Java มี XOR โดยใช้อักขระ ^ (caret)
veidelis

7

ใช่เพียงทำต่อไปนี้ สมมติว่าคุณกำลังจัดการกับ booleans A และ B ดังนั้นค่า A 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

คำตอบนี้ไม่สามารถใช้ได้กับข้อมูลทุกประเภท (เช่นคำตอบของ Premchandra) เช่น!(2 === 3)เป็นtrueแต่2และ3มีtruthyดังนั้นควรจะเป็น2 XOR 3 false
Mariano Desanze

2
หากคุณอ่านข้อความของฉันอย่างระมัดระวังมากขึ้นคุณจะสังเกตเห็นว่าฉันเขียนว่า "ถ้าคุณจัดการกับ booleans A และ B ... "
asiby

5

เช็คเอาท์:

คุณสามารถเลียนแบบได้ดังนี้:

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

3
สวัสดีถ้าพวกเขาเพิ่มตัวดำเนินการ XOR แบบลอจิคัลใน JavaScript มันจะทำให้ตัวอย่างโค้ดดูสะอาดตาขึ้น
Danyal Aytekin

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" ที่ง่ายที่สุดและเร็วที่สุด
Doin

หากคุณหมายถึงการแสดงออกครั้งสุดท้ายคุณstate1 !== state2ไม่จำเป็นต้องทำการคัดเลือกนักแสดงที่นั่นเพราะ!==เป็นผู้ดำเนินการเชิงตรรกะไม่ใช่บิต 12 !== 4เป็นความจริง'xy' !== trueก็เป็นจริงเช่นกัน หากคุณต้องการใช้!=แทนคุณ!==จะต้องทำการคัดเลือกนักแสดง
Lajos Meszaros

1
ผลลัพธ์ของทั้งคู่!==และ!=เป็นบูลีนเสมอ ... ไม่แน่ใจว่าความแตกต่างที่คุณทำนั้นควรจะเป็นอะไรนั่นไม่ใช่ปัญหาอย่างแน่นอน ปัญหาคือผู้ดำเนินการ XOR ที่เราต้องการนั้นเป็นนิพจน์(Boolean(state1) !== Boolean(state2))อย่างแท้จริง สำหรับ booleans "เซ็กซี่", 12, 4 และtrue เป็นค่า truthy trueทั้งหมดและควรแปลง ดังนั้น("xy" XOR true)ควรจะเป็นfalseแต่("xy" !== true)เป็นแทนtrueตามที่คุณชี้ให้เห็น ดังนั้น!==หรือเท่ากับ!=(ทั้งสอง) เทียบเท่ากับ "XOR แบบลอจิคัล" หากว่าคุณแปลงอาร์กิวเมนต์เป็น booleans ก่อนที่จะใช้
Doin

2

ในฟังก์ชั่น xor ด้านบนมันจะส่งผลให้ผลลัพธ์SIMILARเป็นตรรกะ xor ไม่ได้ว่าตรรกะ xor อย่างแน่นอนหมายความว่ามันจะส่งผล"เท็จสำหรับค่าเท่ากัน"และ"เป็นจริงสำหรับค่าที่แตกต่าง"กับการจับคู่ชนิดข้อมูลในการพิจารณา

ฟังก์ชั่น xor นี้จะทำงานเป็น xor จริงหรือผู้ประกอบการตรรกะหมายถึงว่ามันจะส่งผลตามจริงหรือเท็จกับค่าผ่านมี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)งานได้ดีกับ booleans ใน typescript + !!(+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 สำหรับค่าความจริง / เท็จเช่นเดียวกับที่คุณมีโอเปอเรเตอร์ในคำสั่ง 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.

หากคุณไม่สนใจเกี่ยวกับการนับหรือถ้าคุณสนใจประสิทธิภาพที่ดีที่สุด: ให้ใช้ bitor xor กับค่าที่บังคับให้บูลีนสำหรับวิธีแก้ปัญหาความจริง / เท็จ

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 ทำ และฉันพบการใช้งานมากขึ้นในสถานการณ์เช่นนี้ แต่ฉันกำลังเขียน xor แบบลอจิคัลจริงในคำตอบอื่นด้านล่าง
Premchandra Singh
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.