“ ในขณะที่ (จริง)” ลูปไม่ดีเหรอ? [ปิด]


218

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

do{
     //get some input.
     //if the input meets my conditions, break;
     //Otherwise ask again.
} while(true)

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

ฉันเข้าใจถึงข้อผิดพลาดgotoและลูกพี่ลูกน้องของ Java อย่างสมบูรณ์break:labelและฉันรู้สึกดีที่จะไม่ใช้มัน ฉันยังตระหนักว่าโปรแกรมที่สมบูรณ์กว่านี้จะให้วิธีการหลบหนีอื่น ๆ เช่นเพื่อจบโปรแกรม แต่นั่นไม่ใช่เหตุผลที่อาจารย์ของฉันอ้างดังนั้น ...

มีอะไรผิดปกติกับdo-while(true)?


23
ถามอาจารย์ของคุณว่าเนื้อหาแบบนั้นเป็นเรื่องส่วนตัว
Chris Eberle

18
ฉันได้พบบทความนี้เป็นประโยชน์ที่จะเข้าใจสิ่งที่เป็นอันตรายเกี่ยวกับการข้ามไป การเปรียบเทียบกับbreakอาจมีความหมายดี แต่เข้าใจผิดจริง บางทีคุณสามารถให้การศึกษาแก่อาจารย์ของคุณเกี่ยวกับเรื่องนี้;) ในประสบการณ์ของฉันอาจารย์ไม่ค่อยรู้เรื่องการเขียนโปรแกรม
แมกนัสฮอฟฟ์

100
สิ่งที่เลวร้ายจริงๆที่ไม่อาจโต้แย้งได้เกี่ยวกับเรื่องนี้คือในใจของฉันความจริงที่do {} while (true)เทียบเท่าwhile(true) {}และแบบหลังนั้นอยู่ในรูปแบบที่ธรรมดากว่าและชัดเจนกว่ามาก
Voo

11
หากใครไม่พอใจกับพลังการแสดงออกอย่างง่ายbreakพวกเขาควรลองเขียนโปรแกรมในภาษาที่ไม่มีมัน ไม่ใช้ลูปมากเกินไปก่อนที่คุณจะต้องการมัน!
Steven

16
ฉันไม่เห็นด้วยกับแท็กการบ้าน
aaaa bbbb

คำตอบ:


220

ฉันจะไม่พูดว่ามันไม่ดี - แต่อย่างน้อยฉันก็มักจะมองหาทางเลือกอื่น

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

เป็นตัวอย่างที่ชัดเจนว่าจะใช้breakมากกว่าธงให้พิจารณา:

while (true)
{
    doStuffNeededAtStartOfLoop();
    int input = getSomeInput();
    if (testCondition(input))
    {
        break;
    }
    actOnInput(input);
}

ตอนนี้เราบังคับให้ใช้แฟล็ก:

boolean running = true;
while (running)
{
    doStuffNeededAtStartOfLoop();
    int input = getSomeInput();
    if (testCondition(input))
    {
        running = false;
    }
    else
    {
        actOnInput(input);
    }
}

ฉันดูหลังที่ซับซ้อนกว่าในการอ่าน: มันมีelseบล็อกactOnInputเพิ่มเติมมีการเยื้องมากขึ้นและถ้าคุณกำลังพยายามหาสิ่งที่จะเกิดขึ้นเมื่อtestConditionกลับtrueมาคุณต้องมองอย่างระมัดระวังผ่านส่วนที่เหลือของบล็อกเพื่อตรวจสอบว่ามี ไม่ได้เป็นสิ่งหลังจากelseบล็อกซึ่งจะเกิดขึ้นไม่ว่าจะrunningได้รับการตั้งค่าให้falseหรือไม่

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

โปรดทราบว่านี่เป็นอาร์กิวเมนต์ประเภทเดียวกับที่ผู้ใช้มีคำสั่ง return หลายคำสั่งในวิธีการ ตัวอย่างเช่นถ้าฉันสามารถหาผลลัพธ์ของวิธีการภายในสองสามบรรทัดแรก (เช่นเพราะอินพุตบางส่วนเป็นโมฆะหรือว่างเปล่าหรือศูนย์) ฉันพบว่ามันชัดเจนกว่าที่จะส่งคืนคำตอบนั้นโดยตรงกว่าจะมีตัวแปรเก็บผลลัพธ์ จากนั้นบล็อกทั้งหมดของรหัสอื่นและในที่สุดก็มีreturnคำสั่ง


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

3
@ X-Zero: ใช่บางครั้ง หากคุณสามารถเปลี่ยน "หยุด" เป็น "คืน" ที่มักจะดี ... แม้ว่าคุณอาจจะยังคงจบลงด้วยwhile (true)
Jon Skeet

21
@trutheality: ฉันต้องการรหัสส่วนใหญ่ของฉันเป็น unindented เป็นไปได้ว่า และเงื่อนไขที่มีการมอบหมายผสมรู้สึกว่าซับซ้อนกว่าสำหรับฉัน
Jon Skeet

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

6
@Ismail: ฉันหวังว่าไม่ โพสต์ควรได้รับการตัดสิน แต่เพียงผู้เดียวในเนื้อหาของพวกเขาไม่ใช่ผู้เขียน Heck ฉันจะลงคะแนนโพสต์ Eric Lippert ถ้าฉันรู้สึกว่ามันไม่ถูกต้อง / ไม่ช่วยเหลือ นั่นยังไม่เกิดขึ้น :)
Jon Skeet

100

AFAIK ไม่มีอะไรจริงๆ ครูก็แพ้gotoเพราะพวกเขาได้ยินบางที่มันแย่จริงๆ มิฉะนั้นคุณก็จะเขียน:

bool guard = true;
do
{
   getInput();
   if (something)
     guard = false;
} while (guard)

ซึ่งเกือบจะเหมือนกัน

บางทีนี่อาจจะสะอาดกว่า (เพราะข้อมูลการวนซ้ำทั้งหมดอยู่ที่ด้านบนของบล็อก):

for (bool endLoop = false; !endLoop;)
{

}

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

4
ฉันทามติคือธงชาตินั้นดีกว่าแตกหักเพราะมันแสดงเจตนา ฉันเห็นว่าเป็นประโยชน์ คำตอบที่ชัดเจนทั้งหมด แต่ฉันจะทำเครื่องหมายว่าเป็นที่ยอมรับ
JHarnach

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

8
ฉันชอบคำตอบของ Jon Skeet ที่ดีกว่า นอกจากนี้ในขณะที่ (จริง) {} จะดีกว่ามาก {} ในขณะที่ (จริง)
aaaa bbbb

1
ฉันเกลียด Dijkstra ที่เคยเขียนบทความ GoTo ในขณะที่ GoTo สามารถถูกทารุณกรรมในอดีตไม่ได้หมายความว่าคุณไม่ควรใช้ นอกจากนี้ Exit For, Break, Exit while, Try / Catch ล้วน แต่เป็นรูปแบบเฉพาะของ Goto Gotos มักทำให้โค้ดอ่านง่ายขึ้น และใช่ฉันรู้ว่าทุกสิ่งสามารถทำได้โดยไม่ต้องไป ไม่ได้หมายความว่าควรจะเป็น
Kibbee

39

Douglas Crockford มีข้อสังเกตเกี่ยวกับวิธีที่เขาต้องการJavaScriptมีloopโครงสร้าง:

loop
{
  ...code...
}

และฉันไม่คิดว่าJavaจะเลวร้ายไปกว่าการมีloopโครงสร้างเช่นกัน

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

แต่สิ่งที่พวกเขาไม่ค่อยพูดถึงคือกลไกการวนซ้ำทั้งหมดสามารถทำซ้ำได้ด้วยwhile(true)ลูป

while( a() )
{
  fn();
}

เป็นเช่นเดียวกับ

loop
{
  if ( !a() ) break;
  fn();
}

และ

do
{
  fn();
} while( a() );

เหมือนกับ:

loop
{
  fn();
  if ( !a() ) break;
}

และ

for ( a(); b(); c() )
{
  fn();
}

เหมือนกับ:

a();
loop
{
  if ( !b() ) break;
  fn();
  c();
}

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

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


2
+1: มีความซับซ้อนเพิ่มเติมพร้อมforลูปเมื่อcontinueมีคำสั่งเกี่ยวข้อง แต่ไม่ได้เป็นส่วนขยายที่สำคัญ
Donal Fellows

ฉันมักจะใช้สำหรับวนรอบเมื่อมีลำดับที่ฉันต้องการเรียกใช้และในขณะที่วนรอบเมื่อมีเงื่อนไขที่ฉันต้องการพบ วิธีนี้จะช่วยให้รหัส imo ชัดเจนมากขึ้น
dascandy

4
ไม่มีใครเขียนfor (;;) {อีกแล้วใช่ไหม (ออกเสียงว่า "ถาวร") เรื่องนี้เคยเป็นที่นิยมมาก
Dawood ibn Kareem

16

ย้อนกลับไปในปี 1967 Edgar Dijkstra เขียนบทความในนิตยสารการค้าว่าเหตุใดจึงควรยกเลิกการข้ามไปจากภาษาระดับสูงเพื่อปรับปรุงคุณภาพรหัส กระบวนทัศน์การเขียนโปรแกรมทั้งหมดที่เรียกว่า "การเขียนโปรแกรมที่มีโครงสร้าง" ออกมาจากสิ่งนี้ แต่แน่นอนว่าทุกคนไม่เห็นด้วยที่ข้ามไปโดยอัตโนมัติหมายถึงรหัสที่ไม่ดี

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

เห็นได้ชัดว่านี่ไม่ใช่กระบวนทัศน์การเขียนโปรแกรมเพียงอย่างเดียว แต่บ่อยครั้งที่มันสามารถนำไปใช้กับกระบวนทัศน์อื่น ๆ เช่นการเขียนโปรแกรมเชิงวัตถุ (ala Java) ได้อย่างง่ายดาย

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

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

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

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


2
นี่คือกระดาษ Edsger Dijkstra ของ มันเป็นการอ่านที่ดี
Roy Prins

14

แบบแผน ususal Java สำหรับการอ่านอินพุตคือ:

import java.io.*;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String strLine;

while ((strLine = br.readLine()) != null) {
  // do something with the line
}

และการประชุม C ++ ปกติสำหรับการอ่านอินพุตคือ:

#include <iostream>
#include <string>
std::string data;
while(std::readline(std::cin, data)) {
  // do something with the line
}

และใน C ก็คือ

#include <stdio.h>
char* buffer = NULL;
size_t buffer_size;
size_t size_read;
while( (size_read = getline(&buffer, &buffer_size, stdin)) != -1 ){
  // do something with the line
}
free(buffer);

หรือถ้าคุณมั่นใจว่าคุณรู้ว่าข้อความที่ยาวที่สุดในไฟล์ของคุณคืออะไรคุณสามารถทำได้

#include <stdio.h>
char buffer[BUF_SIZE];
while (fgets(buffer, BUF_SIZE, stdin)) {
  //do something with the line
}

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

import java.io.*;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line;

while ((line = br.readLine()) != null  && !line.equals("quit") ) {
  // do something with the line
}

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


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

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

@poolie: ถ้าอย่างนั้นก็ควรที่จะใช้คำสั่ง read เป็นเงื่อนไข loop (ซึ่งตรวจสอบ EOF) และตรวจสอบเงื่อนไขอื่น ๆ ของคุณภายใน loop เป็นคำสั่ง break
Ken Bloom

1
สำหรับสิ่งที่คุ้มค่าgets()ไม่ใช่การประชุมทั่วไป มันมีประโยชน์อย่างมากในการบัฟเฟอร์โอเวอร์โฟลว์ fgets(buffer, BUFFER_SIZE, file)เป็นเหมือนการปฏิบัติตามมาตรฐาน
เดฟ

@Dave: fgetsผมได้แก้ไขในขณะนี้คำตอบให้กับการใช้งาน
Ken Bloom

12

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

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

ที่ถูกกล่าวว่าคุณจะยังคงเห็นสิ่งนี้ในรหัสจำนวนมากออกมีในโลกแห่งความจริง


4
ถ้าลูปเริ่มต้นด้วยwhile (true)มันค่อนข้างชัดเจนว่าจะมีbreakหรือreturnอยู่ข้างในหรือจะวิ่งไปเรื่อย ๆ การเป็นคนตรงไปตรงมานั้นสำคัญ แต่while(true)ก็ไม่ได้เลวร้ายอะไร ตัวแปรที่มีค่าคงที่ที่ซับซ้อนในการวนซ้ำวนซ้ำจะเป็นตัวอย่างของสิ่งที่ทำให้เกิดความกังวลมากขึ้น
30651 poolie พ.ค.

4
ลองค้นหาสามระดับลึกลงไปในการค้นหาจากนั้นก็แยกย่อย รหัสจะแย่กว่านี้ถ้าคุณไม่เพียงแค่กลับมาจากจุดนั้น
dascandy

11

มันคือปืนกระสุนและเท้าของคุณ ...

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

ปัญหาจะเริ่มในเวลาสุ่มมากในอนาคต มันอาจเกิดจากโปรแกรมเมอร์อื่น อาจเป็นบุคคลที่ติดตั้งซอฟต์แวร์ อาจเป็นผู้ใช้

ทำไม? ฉันต้องหาสาเหตุที่แอพ 700K LOC จะค่อยๆเริ่มเบิร์นซีพียู 100% ของเวลา CPU จนกว่า CPU ทุกตัวจะอิ่มตัว มันเป็นเรื่องที่น่าทึ่งในขณะที่ (จริง) ห่วง มันใหญ่และน่ารังเกียจ แต่ก็ต้มลงไปที่:

x = read_value_from_database()
while (true) 
 if (x == 1)
  ...
  break;
 else if (x ==2)
  ...
  break;
and lots more else if conditions
}

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

แน่นอนว่าโปรแกรมเมอร์ได้กล่าวโทษผู้ใช้ปลายทางโดยไม่เลือกค่าที่โปรแกรมเมอร์คาดไว้ (จากนั้นฉันจะตัดอินสแตนซ์ทั้งหมดของ while (จริง) ในโค้ด)

IMHO มันไม่ใช่การเขียนโปรแกรมการป้องกันที่ดีที่จะใช้การสร้างเช่นในขณะที่ (จริง) มันจะกลับมาหลอกหลอนคุณ

(แต่ฉันจำอาจารย์ที่ให้คะแนนถ้าเราไม่ได้แสดงความคิดเห็นทุกบรรทัดแม้สำหรับ i ++;)


3
+1 สำหรับความคิดเห็นเกี่ยวกับการตั้งโปรแกรมป้องกัน
JHarnach

3
ในตัวอย่างของคุณรหัสนั้นไม่ได้โง่เพราะตัวเลือกของ while (จริง) แต่เนื่องจากแนวคิดของการวนรอบโค้ด
Florian F

ที่จริงแล้วประเด็นของการวนซ้ำนั้นคืออะไร? :)
juzzlin

6

มันไม่ดีในแง่ที่ว่าการเขียนโปรแกรมโครงสร้างมีความต้องการที่จะทำลายงบ (และค่อนข้างไม่มีโครงสร้าง) และดำเนินการต่อ โดยเปรียบเทียบแล้วชอบที่จะ "goto" ตามหลักการนี้

ฉันขอแนะนำให้สร้างโค้ดของคุณให้มีโครงสร้างมากที่สุดเท่าที่จะเป็นไปได้ ... ถึงแม้ว่า Jon Skeet จะชี้ให้เห็นอย่าทำให้มันมีโครงสร้างมากกว่านั้น!


5

จากประสบการณ์ของฉันในกรณีส่วนใหญ่ลูปมีเงื่อนไข "หลัก" เพื่อดำเนินการต่อ นี่คือเงื่อนไขที่ควรเขียนลงในขณะที่ () ตัวดำเนินการเอง เงื่อนไขอื่น ๆ ทั้งหมดที่อาจทำให้เกิดการวนซ้ำเป็นเรื่องรองไม่สำคัญนัก ฯลฯ สามารถเขียนเป็นif() {break}ข้อความเพิ่มเติมได้

while(true) มักจะสับสนและอ่านไม่ได้

ฉันคิดว่ากฎเหล่านี้ไม่ครอบคลุม 100% ของกรณี แต่อาจจะมีเพียง 98% ของทั้งหมด


พูดได้ดี. มันเหมือนกับการใช้การทำลูปด้วย: while (จริง) {i ++; ... } คุณกำลังฝังเงื่อนไขลูปภายในลูปมากกว่าใน 'ลายเซ็น'
LegendLength

3

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

เกี่ยวกับคำถามของคุณ: ไม่มีปัญหาโดยธรรมชาติ

while(true) {
   do_stuff();
   if(exit_time) {
      break;
   }
}

... ถ้าคุณรู้ว่าสิ่งที่คุณทำและทำให้แน่ใจว่าน้ำพระทัยในบางจุดที่ประเมินexit_timetrue

ครูไม่แนะนำให้คุณใช้while(true)เพราะจนถึงหรือจนกว่าคุณจะถึงจุดที่คุณรู้ว่ากำลังทำอะไรอยู่มันเป็นวิธีที่ง่ายในการทำผิดพลาดร้ายแรง


ฉันคิดว่าการตั้งค่าexit_time = false; while(!exit_time) { execute_stuff(); }และdo { execute_stuff(); } while(! exit_time );ชัดเจนกว่าการมีif( condition ) { break; }while(true)ในตอนท้ายของวงกับที่ ตัวแบ่งเป็นวงจรลัดไปสู่ลูป - ปรับได้อย่างสมบูรณ์แบบเมื่อใช้เป็นวงจรลัดในช่วงกลางของลูป แต่คุณควรประเมินเงื่อนไขในขณะที่คำสั่ง vs มีการแบ่งที่ส่วนท้ายของลูป
dr jimbob

ในแง่ของการ์ตูนเรื่องนั้น: ในขณะที่ทำในขณะที่ไม่สามารถทำทุกอย่างในขณะที่ทำได้ แต่ในบางครั้งก็ทำได้ดีกว่าทำในขณะที่ทำได้แม้ในขณะที่สามารถทำได้
Theraot

3

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

มันเป็นคำถามถ้าคุณจริงจังหรือไม่ ตัวแปลงสัญญาณในทางปฏิบัติอาจใช้ตัวแบ่งในสถานการณ์ง่าย ๆ

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


8
เนื่องจากการรักษาธงบูลีนอาจไม่ชัดเจนและอาจมีความชัดเจนน้อยลงในประสบการณ์ของฉัน
Jon Skeet

3
@Jon Skeet ทีนี้คำถามของการไปกับ habbit ดี ๆ หรือฝึกตัวเองให้แย่ด้วยการพักเบรค บูลที่เรียกว่า "กำลังทำงาน" ไม่ชัดเจนสำหรับคุณใช่ไหม เห็นได้ชัดและง่ายต่อการแก้ปัญหาและตามที่ฉันกล่าวถึงก่อนที่จะ habbit ดีเป็นสิ่งที่ดีเพื่อให้ ปัญหาอยู่ที่ไหน
Daniel Leschkowski

4
บูลที่เรียกว่าการทำงานนั้นต้องการให้ฉันมีif (running)ภายในวงเยื้องส่วนที่เหลือทั้งหมดของรหัสเมื่อทั้งหมดที่ฉันต้องการคือการออกจากวงแน่นอนน้อยชัดเจนกับผมกว่าคำสั่งแบ่งง่าย ๆ ซึ่งระบุว่าสิ่งที่ฉันต้องการจะทำ ดูเหมือนว่าคุณจะพิจารณาใช้แบ่งเป็นนิสัยที่ไม่ดีaxiomatically - ฉันไม่คิดว่ามันเป็นเช่นนั้น
Jon Skeet

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

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

3

บางทีฉันอาจโชคร้าย หรือบางทีฉันแค่ขาดประสบการณ์ แต่ทุกครั้งที่ฉันจำได้ว่าต้องจัดการกับสิ่งwhile(true)ที่breakอยู่ภายในมันเป็นไปได้ที่จะปรับปรุงโค้ดที่ใช้วิธีการแยกเป็นแบบบล็อกซึ่งเก็บไว้while(true)แต่ (โดยบังเอิญ?) เปลี่ยนสิ่งต่างbreakๆ ให้กลายเป็นreturns

จากประสบการณ์ของฉันwhile(true)โดยไม่หยุดพัก (เช่นมีการส่งคืนหรือส่ง) ค่อนข้างสะดวกสบายและเข้าใจง่าย


  void handleInput() {
      while (true) {
          final Input input = getSomeInput();
          if (input == null) {
              throw new BadInputException("can't handle null input");
          }
          if (input.isPoisonPill()) {
              return;
          }
          doSomething(input);
      }
  }

3

ฉันคิดว่าใช่มันไม่ดีนัก ... หรืออย่างน้อยสำหรับนักพัฒนาหลายคน เป็นอาการของนักพัฒนาที่ไม่ได้คิดถึงสภาพของลูป ดังนั้นจึงเกิดข้อผิดพลาดได้ง่าย


2

ไม่มีปัญหาที่สำคัญwhile(true)กับbreakคำสั่ง แต่บางคนอาจคิดว่ามันลดลงเล็กน้อยอ่านรหัส พยายามตั้งชื่อตัวแปรที่มีความหมายประเมินนิพจน์ในตำแหน่งที่เหมาะสม

สำหรับตัวอย่างของคุณดูเหมือนชัดเจนมากขึ้นที่จะทำสิ่งที่ชอบ:

do {
   input = get_input();
   valid = check_input_validity(input);    
} while(! valid)

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

บางทีคุณอาจต้องการผลลัพธ์ที่แตกต่างในครั้งที่สองผ่านลูป สิ่งที่ต้องการ

input = get_input();
while(input_is_not_valid(input)) {
    disp_msg_invalid_input();
    input = get_input();
}

ดูเหมือนจะอ่านได้มากขึ้นสำหรับฉันแล้ว

do {
    input = get_input();
    if (input_is_valid(input)) {
        break;
    }
    disp_msg_invalid_input();
} while(true);

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


1

ฉันใช้สิ่งที่คล้ายกัน แต่ด้วยตรรกะตรงข้ามในฟังก์ชั่นของฉันมากมาย

DWORD dwError = ERROR_SUCCESS;

do
{
    if ( (dwError = SomeFunction()) != ERROR_SUCCESS )
    {
         /* handle error */
         continue;
    }

    if ( (dwError = SomeOtherFunction()) != ERROR_SUCCESS )
    {
         /* handle error */
         continue;
    }
}
while ( 0 );

if ( dwError != ERROR_SUCCESS )
{
    /* resource cleanup */
}

1

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


1

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


1

สำหรับฉันปัญหาคือการอ่าน

คำสั่ง while พร้อมเงื่อนไขจริงจะบอกอะไรคุณเกี่ยวกับลูป มันทำให้งานของการทำความเข้าใจมันยากขึ้น

อะไรจะง่ายกว่าที่จะเข้าใจตัวอย่างโค้ดสองอันนี้

do {
  // Imagine a nice chunk of code here
} while(true);

do {
  // Imagine a nice chunk of code here
} while(price < priceAllowedForDiscount);

0

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


0

1) ไม่มีอะไรผิดปกติกับ do -while(true)

2) อาจารย์ของคุณผิด

NSFs !!:

3) ครูส่วนใหญ่เป็นครูและไม่ใช่โปรแกรมเมอร์


@ คอนเนลรอจนกระทั่งครูของคุณได้ยินนั่น!
Pacerier

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

@Connell โพสต์ของฉันเพื่อแสดงข้อตกลงของคุณ
Pacerier

2
"ผู้ที่ไม่สามารถทำได้จงสอน" - นั่นเป็นลักษณะทั่วไปที่ค่อนข้างใหญ่คุณไม่คิดเหรอ? แพทย์, วิศวกร, นักบินและอื่น ๆ ควรได้รับการสอนด้วยตนเองหรือไม่เนื่องจากอาจารย์ของพวกเขาไร้ความสามารถตามที่คุณต้องการ?
filip-fku

@ filip-fku ว้าวว้าวววว ~ เท่มากเลย
Pacerier

0

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

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