เมื่อใดที่เหมาะสมที่จะใช้ตัวดำเนินการระดับบิตในนิพจน์เงื่อนไข


15

ก่อนอื่นมีภูมิหลังบางอย่าง: ฉันเป็นครูฝึกอบรมด้านไอทีและฉันพยายามแนะนำผู้ประกอบการบูลีนของ java กับชั้นเรียนเกรด 10 ของฉัน ครูที่ปรึกษาของฉันตรวจสอบแผ่นงานที่ฉันเตรียมและแสดงความคิดเห็นว่าฉันสามารถให้พวกเขาใช้เพียง & & หรือเดียว เพื่อแสดงโอเปอเรเตอร์เพราะพวกเขา "ทำสิ่งเดียวกัน"

ฉันตระหนักถึงความแตกต่างระหว่าง & และ &&
& เป็นตัวดำเนินการระดับบิตสำหรับใช้ระหว่างจำนวนเต็มเพื่อดำเนินการ "bit-twiddling"
&& เป็นตัวดำเนินการตามเงื่อนไขที่มีไว้สำหรับใช้ระหว่างค่าบูลีน

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

boolean bitwise;
boolean conditional;
int i=10, j=12;
bitwise = (i<j) | ((i=3) > 5); // value of i after oper: 3
System.out.println(bitwise+ " "+ i);
i=10; 
conditional = (i<j) || (i=3) > 5 ;  // value of i after oper: 10
System.out.println(conditional+ " "+ i);
i=10; 
bitwise = (i>j) & (i=3) > 5;   // value of i after oper: 3
System.out.println(bitwise+ " "+ i);
i=10; 
conditional = (i>j) && (i=3) > 5;  // value of i after oper: 10
System.out.println(conditional+ " "+ i);

ตัวอย่างนี้แสดงให้เห็นว่าหากค่าจะต้องมีการเปลี่ยนแปลงโดยในช่วงครึ่งหลังของการแสดงออกนี้จะนำไปสู่ความแตกต่างระหว่างผลลัพธ์เนื่องจาก bitwise เป็นผู้ประกอบการกระตือรือร้นในขณะที่เงื่อนไขทำงานเป็นลัดวงจร (ไม่ประเมินที่สอง ครึ่งถ้าครึ่งแรกเป็นเท็จในกรณีของ && และเป็นจริงในกรณีของ ||)

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

ฉันสามารถอธิบายตัวเลือกของฉันในการสนับสนุนการใช้ && และ || เกิน & และ เพราะนี่คือการประชุมการเข้ารหัสได้รับการยอมรับในด้านวิศวกรรมซอฟต์แวร์

แต่ใครบางคนได้โปรดให้ฉันดีขึ้นแม้ในโลกแห่งความจริงตัวอย่างการใช้ตัวดำเนินการระดับบิตในนิพจน์เงื่อนไข

คำตอบ:


16

เหมาะสมเมื่อคุณทำการดำเนินการปิดบัง

ถ้า ((& &))> 0) {... }

โดยที่ a และ b เป็นจำนวนเต็ม

|| และ | และ && & และ & ไม่สามารถเปลี่ยนได้

| และ & จะไม่ปรากฏในนิพจน์แบบมีเงื่อนไขด้วยตนเอง (จุดของลิงก์ที่คุณรวมไว้คือสิ่งนั้นมักจะผิดพลาด)

แก้ไข: อย่าเถียงกับที่ปรึกษาของคุณ; แม้ว่าคุณจะชนะคุณก็แพ้ แต่ให้อธิบายว่าคุณไม่ต้องการให้นักเรียนสับสนโดยการผสมตัวดำเนินการเชิงตรรกะกับตัวดำเนินการระดับบิตในบทเรียนเดียวกัน คุณสามารถอธิบายได้ว่าถ้า i = 3 และ j = 2 ดังนั้นฉัน & j = 2 ในขณะที่ฉัน & j เป็นข้อผิดพลาด อย่างไรก็ตามคำอธิบายที่ง่ายกว่าคือคุณกำลังสอนตัวดำเนินการบูลีน (ตรรกะ) ดังนั้นการโยนในกรณีที่มีค่าบิตบิตพิเศษเป็นสิ่งที่ทำให้ไขว้เขวจากประเด็นหลักของบทเรียน ไม่จำเป็นต้องทำให้ผู้ให้คำปรึกษา "ผิด" และไม่จำเป็นต้องสร้างตัวอย่างตอบโต้ จุดเน้นของบทเรียนอยู่ที่โอเปอเรเตอร์บูลีนไม่ใช่ตัวดำเนินการระดับบิต

เมื่อคุณเริ่มสอนผู้ประกอบการระดับบิตคุณไม่จำเป็นต้องแสดงกรณีพิเศษที่ + และ - ให้ผลลัพธ์เช่นเดียวกับ & และ |


รหัสข้างต้นไม่ผิดแม้ว่ามันอาจจะสับสน แต่เนื่องจากว่ารหัสนั้นไม่มีข้อผิดพลาดเกิดขึ้น? ฉันยอมรับว่าการใช้ตัวดำเนินการ bitwise ด้วยวิธีนี้ไม่สามารถอ่านได้มากฉันไม่ชอบถ้าฉันเห็นในการตรวจสอบโค้ด แต่เป็นจาวาที่ถูกกฎหมาย (และ C # ด้วยโดยไม่สนใจ System.out.println)
Steve

ขอบคุณที่ตอบ @Steven A. Lowe คุณพูดว่า "|| และ | และ &&& & & & ไม่สามารถใช้แทนกันได้" แต่bitwise = !(true & true == false);และcondition = !(true && true == false);ทั้งคู่จะประเมินเป็นจริงดังนั้นในกรณีนี้พวกเขาสามารถใช้แทนกันได้หรือไม่ อาจเป็นเพราะมีการรวบรวมรหัส ฉันจะยอมรับว่าพวกเขาใช้สำหรับสิ่งต่าง ๆ ทางความหมายตามที่ฉันกล่าวไว้ในวรรค 2 คุณพูดอย่างนั้น! และ & "แทบจะไม่ปรากฏในเงื่อนไขด้วยตนเอง" ฉันกำลังมองหากรณี "เกือบไม่เคย" เหล่านี้และสงสัยว่าพวกเขามีอยู่จริงหรือไม่
Deerasha

@Deerasha: || และ && ดำเนินการเฉพาะกับ booleans & และ | ทำงานกับจำนวนเต็มและ booleans - มีจุดเล็กน้อยในการใช้ตัวดำเนินการ bitwise (ตั้งใจที่จะทำงานในหลายบิต ) เพื่อจัดการ booleans และการทำเช่นนี้กับการละทิ้งสามารถนำไปสู่การสับสนรหัสและพฤติกรรมที่ไม่คาดคิด (เช่นถ้าคุณบังเอิญ บูลีนผู้ประกอบการระดับบิตจะไม่บ่น)
Steven A. Lowe

ใช่ @Steve Haigh คอมไพเลอร์ไม่ได้ปฏิเสธ แต่มันไม่ใช่วิธีที่ถูกต้องในการใช้งานโดยพิจารณาจากมาตรฐานการเข้ารหัสที่เผยแพร่ที่ฉันเชื่อมโยง ฉันสามารถพักการไล่ออกได้อย่างสะดวกสบายเพราะไม่เป็นไปตามมาตรฐานการเข้ารหัสหรือ Java ควรระบุว่านี่เป็นการใช้ที่ไม่เหมาะสมหรือไม่?
Deerasha

2
@Steven A. Lowe: ถ้า i = 3 และ j = 2 ดังนั้นฉัน & j = 2 ในขณะที่ i &&j เป็นข้อผิดพลาดนี่ยอดเยี่ยมมาก! มันง่ายและทำให้จุด ที่ระดับเกรด 10 นี่เป็นความผิดพลาดที่น่าจะเกิดขึ้นเนื่องจากพวกเขายังคงคุ้นเคยกับประเภทบูลีนและสิ่งที่ผู้ประกอบการจะนำไปใช้ ขอบคุณมาก! คำแนะนำที่ดีเกี่ยวกับการไม่โต้เถียงกับที่ปรึกษาของฉันเช่นกัน
Deerasha

12

ตัวดำเนินการที่ไม่ใช่ระดับบิต&&และ||เป็นตัวดำเนินการลัดวงจร กล่าวอีกนัยหนึ่ง&&หาก LHS เป็นเท็จ RHS จะไม่ถูกประเมิน ด้วย||ถ้า LHS เป็นจริง RHS จะไม่ถูกประเมิน ในทางกลับกันตัวดำเนินการระดับบิต&และ|ไม่ใช่แบบลัดวงจรและมักจะประเมินทั้ง LHS และ RHS มิฉะนั้นจะเทียบเท่าในifคำสั่ง

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


1
+1 ถูกต้องเมื่อเปรียบเทียบ booleans bitwise และตัวดำเนินการเชิงตรรกะกลับผลลัพธ์เดียวกันเสมอ แต่ (ใน Java และ C # อย่างน้อย) เฉพาะตัวดำเนินการทางตรรกะลัดวงจร
Steve

ขอบคุณสำหรับการตอบ วรรค 1 ของคุณคือสิ่งที่ผมพยายามที่จะได้รับในวรรค 4 ของฉันโดยระบุว่าค่าที่เหมาะสมเป็นผู้ประกอบการกระตือรือร้นในขณะที่พฤติกรรมเงื่อนไขเป็นไฟฟ้าลัดวงจร ย่อหน้าที่ 2 ของคุณคือข้อกังวลที่ฉันอธิบายไว้ในย่อหน้าที่ 5 ของฉันดังนั้นฉันจึงทราบถึงความแตกต่าง แต่ฉันกำลังมองหาตัวอย่างเฉพาะนั้นไม่ว่าคุณหรือฉันจะนึกถึงอะไร
Deerasha

7

คำตอบเชิงปรัชญาทั่วไปคือการใช้ตัวดำเนินการ bitwise สำหรับตัวดำเนินการบูลีนผิดปกติและทำให้อ่านรหัสยากขึ้น ในทางปฏิบัติ (สำหรับโค้ดที่ใช้งานจริง) โค้ดที่อ่านได้ง่ายกว่านั้นจะง่ายต่อการบำรุงรักษาและทำให้เป็นที่ต้องการมากกว่า

สำหรับการใช้งานจริงในโลกของความต้องการตัวดำเนินการลัดวงจรดูกรณีเช่น:

if (args.length > 0 && args[0] == 'test') ....

if (b != NULL && b.some_function()) ...

if (b == NULL || b.some_function()) ...

การดำเนินการประเภทนี้แสดงขึ้นบ่อยครั้งในรหัสโลกแห่งความจริง


TFA ฉันจะอธิบายอายุ 15 ปีของฉันที่ 1 ว่า&"อ่านยากขึ้น" กว่า 2 ได้อย่างไร ฉันไม่มีตัวอย่างซึ่งตัวดำเนินการ 2 ตัวไม่ทำงานในลักษณะเดียวกันสำหรับตัวถูกดำเนินการบูลีน ฉันเห็นด้วยกับประเด็นของคุณเกี่ยวกับการอ่านและรักษารหัสได้ง่ายขึ้น ฉันต้องการสนับสนุนให้พวกเขาเขียนโค้ดที่สวยงาม แต่การมีหลักฐานบางอย่างในกระเป๋าเครื่องมือของฉันจะน่าเชื่อถือมากกว่า“ เพราะฉันพูดอย่างนั้น” ฉันได้รับมันระบุว่าเป็นมาตรฐานที่ลิงค์นั้นและอาจต้องพึ่งพาคนเดียวถ้าฉันไม่ได้รับตัวอย่างที่ฉันกำลังมองหา ตามที่ฉันถาม @Steve Haigh: Java ควรระบุว่าเป็นการใช้ที่ไม่เหมาะสมหรือไม่?
Deerasha

@Deerasha ฉันกำลังคิดถึงการโต้เถียงกับที่ปรึกษาของคุณมากกว่า สำหรับชั้นเรียนอย่าพยายามบอกพวกเขาว่าคุณสามารถใช้ตัวดำเนินการระดับบิตสำหรับเงื่อนไขเชิงตรรกะได้
Kathy Van Stone

4

คุณจะใช้ตัวดำเนินการระดับบิตถ้าคุณเปรียบเทียบการระบุ Bitmask ตัวอย่างเช่นคุณมีการระบุสถานะและวัตถุที่สามารถอยู่ในสถานะมากกว่าหนึ่งของรัฐเหล่านั้น ในกรณีนี้คุณจะทำ bitwise หรือกำหนดมากกว่าหนึ่งสถานะให้กับวัตถุของคุณ

ตัวอย่างเช่นstate = CONNECTED | IN_PROGRESSที่ไหนCONNECTED could be 0x00000001และIN_PROGRESS 0x00000010

สำหรับข้อมูลเพิ่มเติมให้ค้นหาเอกสารการตั้งค่าสถานะ enums


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

ฉันไม่คิดว่ามันจะเกิดขึ้นใน Java เพราะฉันเชื่อว่าการเรียก | หรือ & ตัวดำเนินการเกี่ยวกับค่าที่ไม่สามารถเป็นค่าบิตได้และหรือหรือเพียงแค่ทำการตรรกะ | หรือ &. ฉันจำไม่ได้ว่าเป็นกรณีนี้ใน Java หรือไม่ แต่รู้แน่นอนว่าอยู่ใน C # MSDN: "ตัวดำเนินการ Binary | ถูกกำหนดไว้ล่วงหน้าสำหรับ integral ประเภทและ bool สำหรับ integral type, | คำนวณ bitwise OR ของตัวถูกดำเนินการสำหรับ บูลตัวถูกดำเนิน | คำนวณหรือตรรกะของตัวถูกดำเนินการของมัน"
pwny

หลังจากการวิจัยเพิ่มเติมฉันพบว่าความแตกต่างเพียงอย่างเดียวระหว่าง | และ | | และ & และ && & สำหรับตัวถูกดำเนินการบูลีนใน Java เป็นพฤติกรรมการลัดวงจรดังนั้นสถานการณ์ที่คุณกำลังอธิบายไม่เป็นไปได้จริง
pwny

0

ตัวอย่างที่ง่ายกว่าของความผิด:

int condA=1, condB=2;

if (condA!=0 && condB!=0) {
    // correct!
}
if ((condA & condB)!=0) {
    // never executed
}

ที่นี่คุณมีสองเงื่อนไขทั้งสองไม่ใช่ศูนย์ แต่&ผลลัพธ์บิตบิตเป็นศูนย์


@ จาเวียร์: ขอบคุณสำหรับการตอบ แต่ฉันสับสนเล็กน้อย ฉันกำลังทำงานในจาวาและรหัสนี้ไม่ได้รวบรวม (condA && condB)ข้อผิดพลาดเนื่องจาก && ไม่ทำงานเป็นเวลา 2 intวินาทีเพียง 2 บูลีน (condA & condB)ในขณะที่ถูกต้องประเมินเป็นint และในจาวาเราไม่สามารถพูดได้if(int)ดังนั้นข้อผิดพลาดเกินไป คุณเป็นคนแรกที่จะเข้าใจสิ่งที่ฉันกำลังมองหา though- ตรงที่ตัวอย่างของ wrongness
Deerasha

ยังไม่ได้ใช้ Java เป็นเวลานาน ... ลอง(Boolean(condA) && Boolean(condB)) (ฉันคิดว่าBoolean(x)มันtrueเป็นจำนวนเต็มไม่ใช่ศูนย์ใช่มั้ย)
Javier

Nope @Javier: ไม่สามารถร่ายจาก int ไปเป็นบูลีน
Deerasha

แล้ว((condA!=0) && (condB!=0))ไงล่ะ
Javier

@ จาเวียร์ใช่มันรวบรวม แต่ "ความผิด" จะไม่แสดงอีกต่อไป if (((condA!=0) && (condB!=0))) { System.out.println("correct"); } if (((condA!=0) & (condB!=0))) { System.out.println("never executed?"); }รันทั้งสองงบพิมพ์
Deerasha
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.