ภายใน for-loop ฉันควรย้ายเงื่อนไขการพักลงในช่องเงื่อนไขถ้าเป็นไปได้หรือไม่ [ปิด]


48

บางครั้งฉันต้องการลูปที่ต้องการพักเช่นนี้

for(int i=0;i<array.length;i++){
    //some other code
    if(condition){
        break;
    }
}

ฉันรู้สึกอึดอัดกับการเขียน

if(condition){
    break;
}

เพราะมันใช้รหัส 3 บรรทัด และฉันพบว่าลูปสามารถเขียนใหม่เป็น:

                                   ↓
for(int i=0;i<array.length && !condition;i++){
    //some other code
}

ดังนั้นคำถามของฉันคือมันเป็นการดีหรือไม่ที่จะย้ายเงื่อนไขลงในฟิลด์เงื่อนไขเพื่อลดบรรทัดของรหัสถ้าเป็นไปได้?


7
ขึ้นอยู่กับว่าอะไรเป็นconditionพิเศษ
Bergi

4
มีเหตุผลว่าทำไมมิตราห้ามมิให้ใช้การ "หยุด" และ "ดำเนินการต่อ" ภายในลูป ดูเพิ่มเติมได้ที่: stackoverflow.com/q/3922599/476681
BЈовић

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

97
เพราะมันใช้โค้ด 3 บรรทัด A มาก, มาก, เหตุผลที่แย่มาก ๆ ที่ไม่ชอบสไตล์ใด ๆ IMO "consum [ing] บรรทัดของรหัส" อยู่ระหว่างสิ่งที่ไม่เกี่ยวข้องกับสิ่งที่ดี การพยายามใส่ตรรกะมากเกินไปลงในจำนวนบรรทัดน้อยเกินไปจะส่งผลให้เกิดโค้ดที่อ่านไม่ได้และบั๊กที่หายาก
Andrew Henle

3
อาจเป็นความคิดเห็นที่ไม่เป็นที่นิยม: for(int i=0;i<array.length && !condition;i++)ค่อนข้างแปลกและอาจถูกมองข้ามโดยคนที่เพิ่งอ่านรหัส นี่อาจเป็นกรณีการใช้งานที่ดีสำหรับ a whileหรือdo whileloop ซึ่งมักจะมีเงื่อนไขการแบ่งหลายครั้งในการกำหนด loop
จูลส์

คำตอบ:


154

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

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


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

84
@BerinLoritsch: วิธีที่ถูกต้องในการจัดการกับมันคือการไม่มีลูปที่ยาวหลายร้อยบรรทัด!
Chris

27
@ Walrat: ถ้าเขียนใหม่มันไม่ใช่ตัวเลือกถ้าอย่างนั้นก็ไม่มีคำถามอีกแล้ว ต้องบอกว่าคุณสามารถใช้ตัวแยกประเภท "Extract Method" ได้อย่างปลอดภัยเพื่อย้ายบล็อกของโค้ดซึ่งจะทำให้ลูปของเมธอดหลักของคุณสั้นลงและหวังว่าจะทำให้การไหลของลอจิกชัดเจนขึ้น มันเป็นเรื่องจริง ฉันจะยืนตามกฎทั่วไปของการรักษาวิธีการที่สั้นและแน่นอนตรรกะตรรกะต่อไปสั้น ๆ แต่ฉันไม่ต้องการที่จะพยายามพูดอะไรมากกว่านั้นในความหมายทั่วไปแม้ว่า ...
Chris

4
@AndrewHenle ความกะทัดรัดนั้นสามารถอ่านได้อย่างน้อยก็ในแง่ที่ว่าการใช้คำฟุ่มเฟื่อยมากขึ้นจะช่วยลดความสามารถในการอ่านและการบำรุงรักษา การลด LOC ทำได้โดยการเพิ่มความหนาแน่นและเพิ่มระดับนามธรรมและมีความสัมพันธ์อย่างแน่นแฟ้นกับการบำรุงรักษาที่มีส่วนร่วมในคู่ถาม / ตอบอื่น ๆ ในเว็บไซต์นี้
Leushenko

5
@ โจทำในขณะที่สร้างเป็นของหายากที่มันมีแนวโน้มที่จะเดินทางนักพัฒนาที่จำเป็นต้องรักษารหัสนั้นในภายหลัง ผู้พัฒนาบางคนไม่เคยเห็นพวกเขาจริง ๆ ! ฉันไม่แน่ใจว่าสิ่งที่ต้องทำในขณะที่จะปรับปรุงการอ่านทั้งหมด - ถ้ามีอะไรมันจะเพิ่มความยากในการเข้าใจรหัส ตัวอย่างเช่น - codebase ปัจจุบันของฉันมีอายุประมาณ 15 ปีและมี LOC ประมาณ 2 ล้าน นักพัฒนาราวห้าสิบคนแตะมันไปแล้ว มันมีลูปที่ต้องทำเท่าศูนย์เลย!
T. Sar - Reinstate Monica

51

ฉันไม่ได้ซื้ออาร์กิวเมนต์ที่ว่า "มันใช้รหัส 3 บรรทัด" ดังนั้นจึงไม่ดี ท้ายที่สุดคุณสามารถเขียนมันเป็น:

if (condition) break;

และกินเพียงหนึ่งบรรทัด

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

แต่สมมติว่าไม่ใช่กรณีโดยใช้วิธีอื่น:

for(int i=0;i<array.length && !condition;i++){
    //some other code
}

เงื่อนไขการออกจะถูกเก็บไว้ด้วยกันซึ่งสามารถทำให้สิ่งต่าง ๆ ง่ายขึ้น (โดยเฉพาะถ้า " รหัสอื่น ๆ " ยาว)

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


14
@ jpmc26 ดีกว่าไหม ไม่สไตล์ทางเลือกที่ถูกต้อง? แน่นอน.
David Arno

6
ดีกว่าในขณะที่ยากที่จะเลอะเมื่อรหัสถูกแก้ไขในภายหลังและไม่ทุกข์ทรมานจากข้อเสียที่โดดเด่นใด ๆ
jpmc26

4
การรวมเข้าด้วยกันเพราะ "เนื้อหาของลูปอาจยาว" น่าจะเป็นข้อโต้แย้งที่อ่อนแอ เมื่อเนื้อหาของคุณอ่านยากคุณมีปัญหาอื่นนอกเหนือจากการบันทึกรหัสสองบรรทัด
BlueWizard

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

3
ทุกอย่างเป็นสไตล์ที่ชอบ แต่ถ้ามีใครโต้แย้งว่าปลอดภัยกว่าฉันจะไม่เห็นด้วยกับเหตุผลนั้น ตัวฉันเองชอบโดยไม่มีเครื่องหมายปีกกาและในบรรทัดถัดไป แต่นั่นไม่ได้หมายความว่ามันใช้งานได้มากกว่าหรือน้อยกว่าสไตล์อื่น ๆ
phflack

49

คำถามประเภทนี้ได้ก่อให้เกิดการถกเถียงกันมานานตราบใดที่การเขียนโปรแกรมดำเนินไป ที่จะโยนหมวกของฉันเป็นแหวนที่ฉันต้องการไปสำหรับรุ่นที่มีbreakเงื่อนไขและนี่คือเหตุผล (สำหรับการนี้โดยเฉพาะกรณี):

forห่วงเป็นเพียงมีการระบุค่า iterating ในวง การเข้ารหัสเงื่อนไขการแตกหักภายในนั้นเป็นเพียงการทำให้เกิดตรรกะ IMO

การมีตัวแยกbreakทำให้มันชัดเจนว่าในระหว่างการประมวลผลปกติค่าจะเป็นไปตามที่ระบุในforลูปและการประมวลผลควรดำเนินการต่อยกเว้นที่เข้ามาในสภาพ

ในฐานะที่เป็นพื้นหลังเล็กน้อยฉันเริ่มเขียนโค้ด a breakแล้วใส่เงื่อนไขลงในforลูปเป็นเวลาหลายปี (ภายใต้การดูแลของโปรแกรมเมอร์ยอดเยี่ยม) จากนั้นกลับไปที่breakสไตล์เพราะมันรู้สึกสะอาดกว่า (และเป็นสไตล์ที่แพร่หลายใน รหัส Tomes ต่างๆที่ฉันกินเข้าไป)

มันเป็นอีกหนึ่งสงครามศักดิ์สิทธิ์เหล่านั้นในตอนท้ายของวัน - บางคนบอกว่าคุณไม่ควรแยกออกจากวงวนเทียมมิฉะนั้นมันจะไม่ใช่วงจริง ทำในสิ่งที่คุณรู้สึกว่าอ่านได้ (ทั้งเพื่อตัวคุณเองและผู้อื่น) หากการลดการกดแป้นเป็นสิ่งที่คุณต้องการจริงๆให้ไปเล่นกอล์ฟในวันหยุดสุดสัปดาห์ - เลือกเบียร์ก็ได้


3
หากเงื่อนไขมีชื่อที่ดีเช่น "พบ" (เช่นคุณกำลังมองหาอาร์เรย์ของบางอย่าง) คุณควรใส่ไว้ในหัวของ for for loop
user949300

1
ฉันได้รับการบอกว่าforมีการใช้ลูปเมื่อจำนวนการวนซ้ำเป็นที่รู้จัก (เช่นเมื่อไม่มีเงื่อนไขการแตกหัก แต่จบอาร์เรย์ / รายการ) และwhileเมื่อไม่ได้ใช้ นี่น่าจะเป็นกรณีของการwhileวนซ้ำ หากความกังวลเกี่ยวกับการอ่านคือความกังวลidx+=1ภายในวงจะอ่านง่ายกว่าif/elseบล็อกหนึ่ง
เลฟ

คุณไม่สามารถใส่เงื่อนไขการแบ่งในลูปหากมีรหัสติดตามดังนั้นอย่างน้อยคุณจะต้องเขียน "ถ้า (เงื่อนไข) {พบ = จริง; ดำเนินการต่อ;}
gnasher729

เราคุ้นเคยกับfor(int i=0; i<something;i++)ลูปที่ฉันเดาว่าครึ่งหนึ่งของนักพัฒนาจำไม่ได้จริงๆว่าforลูปสามารถใช้งานได้แตกต่างกันดังนั้นจึงยากที่จะเข้าใจ&&conditionส่วนนี้
Ralf Kleberhoff

@RalfKleberhoff แน่นอน ฉันเห็นผู้พัฒนาใหม่หลายคนประหลาดใจอย่างมากว่าการแสดงออกของคำสั่งไตรภาคนั้นเป็นตัวเลือก ฉันจำไม่ได้แม้แต่ครั้งสุดท้ายที่ฉันเห็นfor(;;)...
Robbie Dee

43

forลูปใช้สำหรับทำซ้ำสิ่งที่1 - พวกมันไม่ใช่แค่ lol-Let's-pull-some-random-stuff-from-the-body-and-put-in-the-the-loop-header ความหมายเฉพาะ:

  • คนแรกคือสำหรับการเริ่มต้นiterator มันอาจจะเป็นดัชนีชี้วัตถุซ้ำหรืออะไรก็ตาม - ตราบใดที่จะใช้สำหรับการทำซ้ำ
  • ที่สองคือการตรวจสอบว่าเราถึงจุดสิ้นสุด
  • ประการที่สามคือการเลื่อน iterator

แนวคิดคือการแยกการจัดการการทำซ้ำ ( วิธีการทำซ้ำ) จากตรรกะภายในวง ( สิ่งที่ต้องทำในแต่ละการทำซ้ำ) หลายภาษามักจะมีลูปสำหรับแต่ละอันที่ช่วยให้คุณคลายรายละเอียดของการวนซ้ำ แต่แม้ว่าภาษาของคุณจะไม่มีโครงสร้างนั้นหรือถ้ามันไม่สามารถใช้ในสถานการณ์ของคุณได้ - คุณควร จำกัด ส่วนหัวของลูป เพื่อจัดการซ้ำ

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

1ซึ่งต่างจากลูปอื่น ๆ นั่นคือ "กำลังรอ" สำหรับบางสิ่งที่จะเกิดขึ้น A for/ foreachloop ควรมีแนวคิดของ "list" ของไอเท็มที่จะวนซ้ำบนแม้ว่ารายการนั้นจะขี้เกียจหรือไม่สิ้นสุดหรือเป็นนามธรรม


5
นี่เป็นความคิดแรกของฉันเมื่อฉันอ่านคำถาม ผิดหวังฉันต้องเลื่อนครึ่งโหลคำตอบเพื่อดูใครบางคนพูดว่า
Nemo

4
อืมม ... ทุกลูปสำหรับการทำซ้ำ - นั่นคือสิ่งที่คำว่า "การทำซ้ำ" หมายถึง :-) ฉันคิดว่าคุณหมายถึงบางสิ่งเช่น "วนรอบคอลเลกชัน" หรือ "วนซ้ำโดยใช้ตัววนซ้ำ"?
psmears

3
@psmears ตกลงฉันอาจเลือกคำผิด - ภาษาอังกฤษไม่ใช่ภาษาแม่ของฉันและเมื่อฉันคิดถึงการทำซ้ำฉันคิดว่า "การทำซ้ำมากกว่าสิ่งที่" ฉันจะแก้ไขคำตอบ
Idan Arye

ตัวพิมพ์กลางคือเงื่อนไขที่เป็นเช่นsomeFunction(array[i])นั้น ตอนนี้เงื่อนไขทั้งสองส่วนเกี่ยวข้องกับสิ่งที่คุณทำซ้ำแล้วซ้ำอีกและมันสมเหตุสมผลที่จะรวมเข้ากับ&&ส่วนหัวของลูป
Barmar

ฉันเคารพตำแหน่งของคุณ แต่ฉันไม่เห็นด้วย ฉันคิดว่าforลูปที่หัวใจเป็นตัวนับไม่วนซ้ำในรายการและนี่สนับสนุนโดยไวยากรณ์ของforภาษาก่อนหน้า (ภาษาเหล่านี้มักจะมีfor i = 1 to 10 step 2ไวยากรณ์และstepคำสั่ง - แม้อนุญาตค่าเริ่มต้นที่ไม่ใช่ดัชนีของกลุ่มแรก องค์ประกอบ - คำแนะนำที่บริบทตัวเลขไม่ใช่บริบทรายการ) เฉพาะกรณีการใช้งานที่ฉันคิดว่าการสนับสนุนforลูปเป็นเพียง iterating กว่ารายการเป็น parallelising และที่ต้องใช้ไวยากรณ์อย่างชัดเจนในขณะนี้อยู่แล้วในการที่จะไม่ทำลายรหัสทำเช่นการจัดเรียง
โลแกน Pickup

8

if (condition)ผมขอแนะนำให้ใช้รุ่นที่มี สามารถอ่านและแก้ไขข้อบกพร่องได้ง่ายขึ้น การเขียนโค้ดพิเศษ 3 บรรทัดจะไม่ทำให้คุณเสีย แต่จะทำให้บุคคลต่อไปเข้าใจรหัสของคุณง่ายขึ้น


1
เฉพาะในกรณีที่ (ชอบแสดงความคิดเห็น) บล็อกลูปไม่มีโค้ดหลายร้อยบรรทัดและตรรกะภายในไม่ใช่วิทยาศาสตร์จรวด ฉันคิดว่าข้อโต้แย้งของคุณก็โอเค แต่ฉันคิดถึงบางnaming thingsอย่างที่เหมาะสมเพื่อที่จะเข้าใจว่าเกิดอะไรขึ้นและเมื่อไรที่สภาพการแตกหักเกิดขึ้น
Laiv

2
@Laiv หากบล็อกลูปมีโค้ดหลายร้อยบรรทัดอยู่ภายในนั้นจะเป็นปัญหาใหญ่กว่าคำถามที่จะเขียนเงื่อนไขการพัก
Aleksander

นั่นเป็นเหตุผลที่ฉันแนะนำบริบทให้กับคำตอบเพราะมันไม่จริงเสมอไป
Laiv

8

หากคุณกำลังวนซ้ำคอลเลกชันที่องค์ประกอบบางอย่างควรจะข้ามไปฉันขอแนะนำตัวเลือกใหม่:

  • กรองคอลเลกชันของคุณก่อนทำซ้ำ

ทั้ง Java และ C # ทำให้เรื่องนี้ค่อนข้างน่าสนใจ ฉันจะไม่แปลกใจถ้า C ++ มีวิธีสร้าง iterator แฟนซีเพื่อข้ามองค์ประกอบบางอย่างที่ไม่ได้ใช้ แต่นี่เป็นเพียงตัวเลือกเท่านั้นหากภาษาที่คุณเลือกรองรับและเหตุผลที่คุณใช้งานbreakอยู่ก็เพราะมีเงื่อนไขว่าคุณจะประมวลผลองค์ประกอบหรือไม่

มิฉะนั้นมีเหตุผลที่ดีมากที่จะทำให้สภาพของคุณเป็นส่วนหนึ่งของ for loop - สมมติว่าการประเมินเบื้องต้นนั้นถูกต้อง

  • จะเห็นได้ง่ายขึ้นเมื่อลูปสิ้นสุดก่อน

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

  • break คำสั่งฝังในตรรกะหายากและบางครั้งก็น่าแปลกใจ
  • การดีบั๊กต้องใช้การวนซ้ำของลูปแต่ละครั้งเพื่อให้เข้าใจอย่างแท้จริงว่าเกิดอะไรขึ้น
  • รหัสจำนวนมากไม่มี refactorings ที่สามารถย้อนกลับได้ง่ายหรือการทดสอบหน่วยเพื่อช่วยในการทำงานเทียบเท่าหลังจากเขียนใหม่

ในสถานการณ์นั้นฉันแนะนำแนวทางต่อไปนี้ตามลำดับความต้องการ:

  • กรองคอลเล็กชันเพื่อกำจัดความต้องการbreakถ้าเป็นไปได้
  • สร้างส่วนที่มีเงื่อนไขของเงื่อนไข for for loop
  • วางเงื่อนไขด้วยbreakที่ด้านบนของลูปถ้าเป็นไปได้ทั้งหมด
  • เพิ่มความสนใจแบบหลายบรรทัดเพื่อดึงดูดความสนใจและทำไมถึงมี

แนวทางเหล่านี้มักจะถูกต้องของรหัสของคุณ เลือกตัวเลือกที่สามารถแสดงเจตนาและปรับปรุงความชัดเจนของรหัสของคุณได้ดีที่สุด


1
ส่วนใหญ่คำตอบนี้ใช้ได้ แต่ ... ฉันสามารถดูได้ว่าการกรองชุดสะสมสามารถขจัดความต้องการcontinueงบได้อย่างไร แต่ฉันไม่เห็นว่าพวกเขาช่วยได้มากเพียงbreakใด
user949300

1
@ user949300 takeWhileตัวกรองสามารถแทนที่breakคำสั่ง หากคุณมีหนึ่ง - Java เช่นเพียงได้รับพวกเขาที่ Jave 9 (ไม่ได้ว่ามันเป็นที่ยากที่จะดำเนินการด้วยตนเอง)
Idan Arye

1
แต่ใน Java คุณยังสามารถกรองก่อนการวนซ้ำ การใช้สตรีม (1.8 หรือใหม่กว่า) หรือใช้ Apache Collections (1.7 ขึ้นไป)
Laiv

@IdanArye - มีส่วนเสริมที่เพิ่มสิ่งอำนวยความสะดวกเช่น Java streams API (เช่นJOO-lambda )
Jules

@IdanArye ไม่เคยได้ยินเรื่อง TakeWhile มาก่อนความคิดเห็นของคุณ การลิงก์ตัวกรองกับการเรียงลำดับอย่างชัดเจนทำให้ฉันเป็นเรื่องผิดปกติและแฮ็คเนื่องจากในตัวกรอง "ปกติ" ทุกองค์ประกอบจะได้รับผลตอบแทนจริงหรือเท็จโดยไม่ขึ้นอยู่กับสิ่งที่เกิดขึ้นก่อนหรือหลัง ด้วยวิธีนี้คุณสามารถทำสิ่งต่าง ๆ ในแบบคู่ขนาน
949300

8

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

ผู้คนไม่ได้อ่านตัวอักษรทุกตัวเมื่ออ่านคำและพวกเขาไม่ได้อ่านทุกคำเมื่ออ่านหนังสือ - ถ้าพวกเขาเก่งในการอ่านพวกเขาจะดูโครงร่างของคำและประโยคและปล่อยให้สมองเติมเต็มในส่วนที่เหลือ .

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

for(int i=0;i<array.length&&!condition;i++)

หากคุณต้องการใช้สไตล์นั้นโดยไม่คำนึงถึงฉันขอแนะนำให้เปลี่ยนชิ้นส่วนfor(int i=0;i<และ;i++)บอกสมองของผู้อ่านว่านี่เป็นมาตรฐานสำหรับลูป


อีกเหตุผลที่จะไปif- breakคือคุณไม่สามารถใช้วิธีการของคุณกับfor-condition คุณต้องใช้if- breakเมื่อเงื่อนไขการแบ่งซับซ้อนเกินไปที่จะซ่อนภายในforคำสั่งหรืออาศัยตัวแปรที่สามารถเข้าถึงได้ภายในforลูปเท่านั้น

for(int i=0;i<array.length&&!((someWidth.X==17 && y < someWidth.x/2) || (y == someWidth.x/2 && someWidth.x == 18);i++)

ดังนั้นหากคุณตัดสินใจที่จะไปกับif- breakคุณจะได้รับความคุ้มครอง แต่ถ้าคุณตัดสินใจที่จะไปกับfor-conditions คุณต้องใช้ทั้งif- breakและfor-conditions เพื่อให้สอดคล้อง, คุณจะต้องย้ายเงื่อนไขไปมาระหว่างfor-conditions และif- breakเมื่อใดก็ตามที่มีการเปลี่ยนแปลงเงื่อนไข


4

การแปลงของคุณกำลังสมมติว่าสิ่งใดก็ตามที่conditionถูกประเมินtrueเป็นเมื่อคุณเข้าสู่ลูป เราไม่สามารถแสดงความคิดเห็นเกี่ยวกับความถูกต้องของสิ่งนั้นในกรณีนี้เพราะมันไม่ได้ถูกกำหนดไว้

เมื่อประสบความสำเร็จในการ "ลดจำนวนบรรทัดของรหัส" ที่นี่คุณสามารถเข้าไปดูได้

for(int i=0;i<array.length;i++){
    // some other code
    if(condition){
        break;
    }
    // further code
}

และใช้การแปลงแบบเดียวกันมาถึง

for(int i=0;i<array.length && !condition;i++){
    // some other code
    // further code
}

การเปลี่ยนแปลงใดที่ไม่ถูกต้องเนื่องจากตอนนี้คุณทำในสิ่งfurther codeที่คุณไม่เคยทำมาก่อน

รูปแบบการแปลงที่ปลอดภัยกว่าคือการแยกsome other codeและประเมินผลconditionไปยังฟังก์ชันที่แยกต่างหาก

bool evaluate_condition(int i) // This is an horrible name, but I have no context to improve it
{
     // some other code
     return condition;
}

for(int i=0;i<array.length && !evaluate_condition(i);i++){
    // further code
}

4

TL; DR ทำสิ่งที่อ่านได้ดีที่สุดในสถานการณ์เฉพาะของคุณ

ลองมาตัวอย่างนี้:

for ( int i = 1; i < array.length; i++ ) 
{
    if(something) break;
    // perform operations
}

ในกรณีนี้เราไม่ต้องการให้โค้ดใด ๆ ใน for for loop ทำงานหากsomethingเป็นจริงดังนั้นการย้ายการทดสอบsomethingไปยังฟิลด์เงื่อนไขนั้นสมเหตุสมผล

for ( int i = 1; i < array.length && !something; i++ )

จากนั้นอีกครั้งขึ้นอยู่กับว่าsomethingสามารถตั้งค่าไว้ที่trueหน้าลูป แต่ไม่ได้อยู่ในลูปนี่อาจทำให้มีความชัดเจนมากขึ้น:

if(!something)
{
    for ( int i = 1; i < array.length; i++ )
    {...}
}

ลองจินตนาการถึงสิ่งนี้:

for ( int i = 1; i < array.length; i++ ) 
{
    // perform operations
    if(something) break;
    // perform more operations
}

เรากำลังส่งข้อความที่ชัดเจนมากที่นั่น หากsomethingกลายเป็นจริงในขณะที่การประมวลผลอาร์เรย์แล้วละทิ้งการดำเนินการทั้งหมด ณ จุดนี้ ในการย้ายเช็คไปยังฟิลด์เงื่อนไขที่คุณต้องทำ:

for ( int i = 1; i < array.length && !something; i++ ) 
{
    // perform operations
    if(!something)
    {
        // perform more operations
    }
}

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

แน่นอนว่ามีรูปทรงที่แตกต่างกันมากมายสำหรับห่วงและสภาพอาจเป็นไปได้ทั้งหมดนี้มีความแตกต่างของตัวเอง

เขียนรหัสเพื่อให้อ่านได้ดี (ซึ่งเห็นได้ชัดว่าเป็นอัตนัย) และรัดกุม ด้านนอกของชุดเข้มงวดของการเข้ารหัสแนวทางทั้งหมด "กฎ" มีข้อยกเว้น เขียนสิ่งที่คุณรู้สึกว่าเป็นการตัดสินใจที่ดีที่สุดและลดโอกาสที่จะเกิดข้อผิดพลาดในอนาคต


2

ในขณะที่มันอาจจะน่าสนใจเพราะคุณเห็นเงื่อนไขทั้งหมดในส่วนหัวของห่วงและbreakอาจทำให้เกิดความสับสนในบล็อกขนาดใหญ่forห่วงเป็นรูปแบบที่โปรแกรมเมอร์ส่วนใหญ่คาดว่าจะวนรอบบางช่วง

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

บ่อยครั้งทางเลือกที่ดีคือใช้ a whileหรือdo {} whileloop ซึ่งตรวจสอบทั้งสองเงื่อนไขสมมติว่าอาร์เรย์ของคุณมีองค์ประกอบอย่างน้อยหนึ่งองค์ประกอบ

int i=0
do {
    // some other code
    i++; 
} while(i < array.length && condition);

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


แม้ว่าคำถามนี้จะมีคำตอบที่ดีอยู่แล้ว แต่ฉันต้องการอัปเกรดโดยเฉพาะเพราะมันเป็นประเด็นสำคัญ: สำหรับฉันแล้วการมีสิ่งอื่นนอกเหนือจากการตรวจสอบที่ถูกผูกไว้อย่างง่ายใน for-loop ( for(...; i <= n; ...)) ทำให้รหัสอ่านยากขึ้นเพราะฉัน ที่จะหยุดและคิดเกี่ยวกับสิ่งที่เกิดขึ้นที่นี่และอาจพลาดเงื่อนไขทั้งหมดในรอบแรกเพราะฉันไม่คิดว่ามันจะอยู่ที่นั่น สำหรับผมเป็นforห่วงก็หมายความว่าคุณกำลังวนลูปกับบางช่วงถ้ามีสภาพทางออกพิเศษก็ควรจะทำอย่างชัดเจนกับหรือคุณสามารถใช้if while
CompuChip

1

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

หากสิ่งที่คุณต้องการคือการหาค่าในอาเรย์ (ตัวอย่างรหัสที่ให้มาค่อนข้างทั่วไป แต่มันอาจจะเป็นรูปแบบนี้) คุณสามารถลองใช้ฟังก์ชั่นที่จัดทำโดยภาษาการเขียนโปรแกรมเพื่อวัตถุประสงค์นั้นโดยเฉพาะ ตัวอย่างเช่น (pseudo-code เนื่องจากไม่ได้ระบุภาษาการเขียนโปรแกรมโซลูชันจะแตกต่างกันไปตามแต่ละภาษา)

array.find(item -> item.valid)

สิ่งนี้ควรย่อโซลูชันให้สั้นลงอย่างเห็นได้ชัดในขณะที่ทำให้โค้ดของคุณง่ายขึ้นโดยเฉพาะสิ่งที่คุณต้องการ


1

ไม่กี่เหตุผลในความคิดของฉันที่บอกว่าคุณไม่ควร

  1. สิ่งนี้จะลดความสามารถในการอ่าน
  2. ผลลัพธ์อาจไม่เหมือนกัน อย่างไรก็ตามสามารถทำได้ด้วยการdo...whileวนซ้ำ (ไม่ใช่while) เนื่องจากมีการตรวจสอบเงื่อนไขหลังจากเรียกใช้รหัสบางอย่าง

แต่เหนือสิ่งอื่นใดพิจารณาสิ่งนี้

for(int i=0;i<array.length;i++){

    // Some other code
    if(condition1){
        break;
    }

    // Some more code
    if(condition1){
        break;
    }

    // Some even more code
}

ทีนี้คุณสามารถบรรลุมันได้โดยการเพิ่มเงื่อนไขเหล่านี้ลงในforเงื่อนไขนั้นหรือไม่


" openion " คืออะไร? คุณหมายถึง " ความเห็น " หรือไม่?
Peter Mortensen

คุณหมายถึงอะไรโดย"เหตุผลน้อยใน openion ที่บอกว่าคุณไม่ควร" ? คุณสามารถทำอย่างละเอียด?
Peter Mortensen

0

ฉันคิดว่าการลบbreakอาจเป็นความคิดที่ดี แต่ไม่ควรบันทึกบรรทัดของรหัส

ข้อได้เปรียบของการเขียนโดยไม่ได้breakคือโพสต์เงื่อนไขของลูปจะชัดเจนและชัดเจน เมื่อคุณเสร็จสิ้นลูปและดำเนินการบรรทัดถัดไปของโปรแกรมเงื่อนไขลูปของคุณi < array.length && !conditionต้องเป็นเท็จ ดังนั้นอาจiมากกว่าหรือเท่ากับความยาวของอาร์เรย์ของคุณหรือconditionเป็นจริง

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

นี่คือเหตุผลดั้งเดิม Edgar Dijkstra เขียนว่า "Goto Considered Harmful" มันไม่ได้เป็นเรื่องของรูปแบบ: การแตกออกจากวงทำให้ยากที่จะเหตุผลอย่างเป็นทางการเกี่ยวกับสถานะของโปรแกรม ฉันได้เขียนข้อความมากมายbreak;ในชีวิตของฉันและเมื่อเป็นผู้ใหญ่ฉันก็เขียนgoto(เพื่อแยกวงในการแสดงที่สำคัญหลายระดับจากนั้นก็ดำเนินการกับสาขาอื่นของแผนภูมิการค้นหา) แต่ผมห่างไกลมีแนวโน้มที่จะเขียนเงื่อนไขreturnคำสั่งจากวง (ซึ่งไม่เคยเรียกใช้รหัสหลังจากวนและดังนั้นจึงไม่สามารถโคลนขึ้นเงื่อนไขการโพสต์ของมัน) breakกว่า

ป.ล.

ในความเป็นจริงการเปลี่ยนรหัสใหม่เพื่อให้พฤติกรรมเดียวกันอาจต้องการรหัสมากกว่าเดิม การปรับโครงสร้างใหม่ของคุณอาจใช้ได้กับรหัสอื่นโดยเฉพาะหรือหากเราพิสูจน์ได้ว่าconditionจะเริ่มต้นเป็นเท็จ แต่ตัวอย่างของคุณเทียบเท่าในกรณีทั่วไปเพื่อ:

if ( array.length > 0 ) {
  // Some other code.
}
for ( int i = 1; i < array.length && !condition; i++ ) {
  // Some other code.
}

หากรหัสอื่นไม่ใช่แบบซับไลน์คุณอาจย้ายไปยังฟังก์ชันเพื่อไม่ให้ซ้ำตัวเองและจากนั้นคุณปรับโครงสร้างลูปของคุณให้เป็นฟังก์ชันแบบเรียกซ้ำ

ฉันรู้ว่านี่ไม่ใช่ประเด็นหลักสำหรับคำถามของคุณและคุณทำให้มันง่าย


ปัญหาไม่ใช่ว่าตัวแบ่งทำให้ยากต่อการโต้แย้งเกี่ยวกับโค้ดปัญหาคือตัวแบ่งที่สุ่มจะทำให้โค้ดกับสิ่งที่ซับซ้อน ถ้าจำเป็นจริง ๆ แล้วก็ไม่ยากที่จะเถียงเพราะการแตก แต่เพราะรหัสต้องทำสิ่งที่ซับซ้อน
gnasher729

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

0

ใช่ แต่ไวยากรณ์ของคุณไม่เป็นทางการและไม่ดี (tm)

หวังว่าภาษาของคุณรองรับบางสิ่งเช่นนี้

while(condition) //condition being a condition which if true you want to continue looping
{
    do thing; //your conditionally executed code goes here
    i++; //you can use a counter if you want to reference array items in order of index
}

2
บางทีฉันควรจะใช้คำที่แตกต่างกันสำหรับ 'เงื่อนไข' ฉันไม่ได้หมายความอย่างแท้จริงว่าหมายถึงเงื่อนไขเดียวกันที่แน่นอนในตัวอย่าง
Ewan

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

2
บางคนเกลียดการสร้างสรรค์ทางเลือก หรือมองปัญหาในทางที่แตกต่างจากที่อยู่ในหัว หรือโดยทั่วไปแล้วลาอัจฉริยะ ฉันรู้สึกเจ็บปวดของคุณครับ!
Martin Maat

1
@sean ขอโทษเพื่อนฉันรู้ว่าคนเกลียดที่จะบอกว่าพวกเขาผิด แต่ฉันแนะนำให้คุณ es.77
Ewan

2
สิ่งนี้จะwhileเน้นไปที่สัตว์ประหลาดในรหัส - ยกเว้นเป็นอย่างอื่นที่ซ่อนอยู่ในรหัสประจำ / ทางโลกีย์ ตรรกะทางธุรกิจอยู่ด้านหน้าและตรงกลางกลศาสตร์การวนซ้ำจะถูกรวมเข้าด้วยกัน มันหลีกเลี่ยงปฏิกิริยา "รอสักครู่ ... " ต่อforทางเลือกของลูป คำตอบนี้แก้ปัญหาได้ โหวตขึ้นอย่างแน่นอน
Radarbob

0

หากคุณกังวลเกี่ยวกับข้อความที่ซับซ้อนมากจนforอ่านยากนั่นเป็นข้อกังวลที่ถูกต้อง การเสียพื้นที่ตามแนวตั้งเป็นปัญหาเช่นกัน (แม้ว่าจะมีความสำคัญน้อยกว่าก็ตาม)

(โดยส่วนตัวแล้วฉันไม่ชอบกฎที่ifข้อความสั่งต้องมีเครื่องหมายปีกกาซึ่งส่วนใหญ่เป็นสาเหตุของพื้นที่แนวตั้งที่สูญเปล่าที่นี่โปรดทราบว่าปัญหาคือการสูญเสียพื้นที่แนวตั้งการใช้พื้นที่แนวตั้งที่มีเส้นที่มีความหมายไม่ควรเป็นปัญหา)

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

for (
   int i = 0, j = 0;
   i < array.length && !condition;
   i++, j++
) {
   // stuff
}

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

array
.takeWhile(condition)  // condition can be item => bool or (item, index) => bool
.forEach(doSomething); // doSomething can be item => void or (item, index) => void

การแยกข้อความที่ซับซ้อนหรือผิดปกติforออกเป็นหลายบรรทัดเป็นความคิดที่ดีที่สุดในการอภิปรายทั้งหมด
949300

0

สิ่งหนึ่งที่ถูกลืม: เมื่อฉันแยกจากลูป (ไม่วนซ้ำที่เป็นไปได้ทั้งหมดไม่ว่าจะนำไปใช้อย่างไร) โดยทั่วไปแล้วมันเป็นกรณีที่ไม่เพียง แต่ฉันต้องการออกจากลูปฉันก็อยากจะรู้ว่าทำไม . ตัวอย่างเช่นหากฉันมองหารายการ X ไม่เพียง แต่ฉันจะแยกจากลูปฉันยังต้องการทราบว่าพบ X และอยู่ที่ไหน

ในสถานการณ์นั้นการมีเงื่อนไขภายนอกลูปนั้นค่อนข้างเป็นธรรมชาติ ดังนั้นฉันเริ่มต้นด้วย

bool found = false;
size_t foundIndex;

และในวงฉันจะเขียน

if (condition) {
    found = true;
    foundIndex = i;
    break;
}

ด้วยสำหรับห่วง

for (i = 0; i < something && ! found; ++i)

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

if (! found) { ... }

ที่ไหนสักแห่งในวงของคุณ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.