จะแยกลูปที่ซ้อนกันได้อย่างไร?


100

ถ้าฉันใช้breakคำสั่งมันจะทำลายวงในเท่านั้นและฉันต้องใช้แฟล็กเพื่อทำลายวงนอก แต่ถ้ามีลูปที่ซ้อนกันมากโค้ดจะดูไม่ดี

มีวิธีอื่นในการทำลายลูปทั้งหมดหรือไม่? (กรุณาอย่าใช้goto stmt)

for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; j++) {
       if(condition) {
            // both of the loops need to break and control will go to stmt2
       }
   }

}

stmt2

2
คุณอาจลอง int i และ int j ก่อนที่ลูปจะเริ่มจากนั้นตามเงื่อนไขทำให้ 1001 ลูปจะไม่วนซ้ำในครั้งต่อไป
Khurram Ijaz

คำตอบ:


49

ใช้:

if (condition) {
    i = j = 1000;
    break;
}

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

1
@ugoren มันง่ายมากเช่นกัน จะเกิดอะไรขึ้นถ้าคุณใช้ a const int count =1000 ในการเริ่มต้นส่วนกลาง หรือเป็น#defineมาโคร
Laksith

4
ดังที่ @ugoren ชี้ให้เห็นว่ามันไม่ใช่วิธีแก้ปัญหาทั่วไป เนื่องจากนี่เป็นคำถามแรกของ Google สำหรับคำถามนี้จึงเป็นการดีหากเลือกวิธีแก้ปัญหาทั่วไป ผู้คนคุ้นเคยกับการตรวจสอบ # 2 อยู่แล้ว
BeeOnRope

1
ฉันเดาว่าต้องการแค่ i = 1000?
Peter Wu

192

ไม่อย่าทำให้เสียความสนุกด้วยไฟล์break. นี่คือการใช้งานที่ถูกต้องล่าสุดที่เหลืออยู่ของgoto;)

หากไม่เป็นเช่นนั้นคุณสามารถใช้แฟล็กเพื่อแยกลูปที่ซ้อนกันลึก ๆ

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

สรุป - เพื่อแยกออกจากลูปที่ซ้อนกัน:

  1. ใช้ goto
  2. ใช้ธง
  3. แยกลูปออกเป็นการเรียกฟังก์ชันแยกกัน

ไม่สามารถต้านทานการรวม xkcd ไว้ที่นี่ :)

ป้อนคำอธิบายภาพที่นี่

แหล่งที่มา

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


31
Goto ชัดเจนที่สุดเท่าที่คุณจะมาถึงที่นี่ใช่ การตั้งค่าตัวแปร exit เป็น 1000 จะยิ่งทำให้เกิดปัญหามากขึ้น
correnos

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

1
ฉันไม่ต้องการเห็นด้วยกับสิ่งนี้: "การสร้างฟังก์ชันจะทำให้เกิดการเพิ่ม / ลบสแต็กพอยน์เตอร์" หากมีฟังก์ชันโลคัล (แบบคงที่) ซึ่งถูกเรียกใช้เพียงจุดเดียวในโฟลว์โปรแกรมคอมไพเลอร์ที่มีค่าครึ่งเดียวใด ๆ จะอินไลน์และโค้ดผลลัพธ์จะเหมือนกับ goto เป็นหลัก นี่อาจเป็นกรณีการเพิ่มประสิทธิภาพที่ง่ายที่สุดสำหรับคอมไพเลอร์ใด ๆ
DrV

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

2
+1. และการเขียนโปรแกรมเชิงโครงสร้างของ Donald E. Knuth ด้วยไปที่ Statements ( wiki.c2.com/?StructuredProgrammingWithGoToStatements ) เป็นบทความที่น่าสนใจในการสร้างสมดุลของ Dijkstra
kmkaplan

41
bool stop = false;
for (int i = 0; (i < 1000) && !stop; i++)
{
    for (int j = 0; (j < 1000) && !stop; j++)
    {
        if (condition)
            stop = true;
    }
}

วิธีแก้ปัญหายังคงเพิ่มตัวแปรทั้งสองทีละตัวซึ่งอาจทำให้เกิดปัญหา
TheSola10

8
สามารถตั้งค่า "stop = true;" แล้ว "ทำลาย;" จากนั้นหลังจากสิ้นสุดด้านใน "for" loop ให้ทำ "if (stop) break;"
Jeff Grigg

1
ฉันคิดว่านี่เป็นgotoวิธีแก้ปัญหาที่ไม่หรูหราที่สุด ไม่จำเป็นต้องรู้ถึงขีด จำกัดiและjภายในเงื่อนไข ผมชอบความคิดของการวางif (stop) break;หลังจากวนภายในซึ่งสามารถตัวเองหลังจากการตั้งค่าbreak stop
Willis Blackburn

35

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

function() 
{    
  for(int i=0; i<1000; i++)
  {
   for(int j=0; j<1000;j++)
   {
      if (condition)
        return;
   }
  }    
}

21

ฉันคิดว่าgotoจะแก้ปัญหาได้

for(int i = 0; i < 1000; i++) {
    for(int j = 0; j < 1000; j++) {
        if (condition) {
            goto end;
        }
    }
}

end:
stmt2 

 

@chikuba ฉันได้รับคำตอบจากcprogramming.com/tutorial/goto.htmlและคำตอบของคุณจะไม่โพสต์เมื่อฉันทำสิ่งเดียวกันนั่นคือเหตุผลที่ฉันไม่เห็นโพสต์ของคุณ
Renjith KN

18

คุณจะต้องมีตัวแปรบูลีนหากคุณต้องการให้อ่านได้:

bool broke = false;
for(int i = 0; i < 1000; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      broke = true;
      break;
    }
  }
  if (broke)
    break;
}

หากคุณต้องการให้อ่านน้อยลงคุณสามารถเข้าร่วมการประเมินบูลีนได้:

bool broke = false;
for(int i = 0; i < 1000 && !broke; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      broke = true;
      break;
    }
  }
}

วิธีที่ดีที่สุดคุณสามารถยกเลิกการวนรอบเริ่มต้นได้:

for(int i = 0; i < size; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      i = size;
      break;
    }
  }
}


4

ข้อควรระวัง: คำตอบนี้แสดงโครงสร้างที่คลุมเครืออย่างแท้จริง

หากคุณใช้ GCC โปรดดูไลบรารีนี้ เช่นเดียวกับใน PHP breakสามารถยอมรับจำนวนลูปที่ซ้อนกันที่คุณต้องการออกได้ คุณสามารถเขียนสิ่งนี้:

for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; j++) {
       if(condition) {
            // break two nested enclosing loops
            break(2);
       }
   }
}

และภายใต้ประทุนนั้นใช้งานgotoจริง:)
jacobq

@ iX3 ฉันสามารถใช้คำสั่งอินไลน์แอสเซมเบลอร์และ jmp ได้หากช่วยได้
DaBler

@DaBler ฉันไม่รู้ว่าคุณเป็นคนเขียนห้องสมุดนั้น ความคิดเห็นของฉันก็ไม่ได้หมายความว่าเป็นข้อเสนอแนะ แต่สังเกตว่าห้องสมุดนี้ใช้วิธีการเดียวกับคำตอบที่ได้รับการยอมรับ หวังว่าความคิดเห็นของคุณจะเป็นเรื่องตลกเพราะฉันคิดว่าการใช้คุณลักษณะภาษา (คู่goto) นั้นดีกว่าที่จะอินไลน์ asm (เฉพาะเครื่องทำผิดพลาดง่ายกว่าอ่านยากกว่า ... )
jacobq


3

หากคุณต้องการค่า i และ j สิ่งนี้ควรใช้งานได้ แต่มีประสิทธิภาพน้อยกว่าค่าอื่น ๆ

for(i;i< 1000; i++){    
    for(j; j< 1000; j++){
        if(condition)
            break;
    }
    if(condition) //the same condition
        break;
}

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

1
คุณพูดถูก แต่หลังจากหยุดพักค่าของjจะไม่เปลี่ยนแปลงดังนั้นค่าของเงื่อนไขก็เช่นกัน
Ali Eren Çelik

นี่เป็นวิธีการแก้ปัญหาที่เสียและไม่สามารถใช้ได้โดยทั่วไป ทั้งเจไม่ได้ถูกกำหนดอยู่นอกวงหรือfor (int i = 0; i < 1000; i++) { for (int j = 0; j < 1000; j++) { if (workComplete[i][j]) break; /* do work */ workComplete[i][j] = true; } if (workComplete[i][j]) break; ... }เป็นไปได้เสมอแยกออกจากนอกวงหลังจากซ้ำแรกของวงชั้นใน
Chai T. Rex

0

วิธีการที่แตกต่างกันคือการ refactor รหัสจากสองสำหรับลูปเป็นสำหรับลูปและหนึ่งลูปแบบแมนนวล วิธีนี้จะใช้การแบ่งในลูปแมนนวลกับวงนอก ฉันเคยใช้สิ่งนี้ครั้งหนึ่งในการกำจัดเกาส์ - จอร์แดนซึ่งต้องใช้ลูปซ้อนกันสามครั้งในการประมวลผล

for (int i = 0; i < 1000; i++)
{
    int j = 0;

MANUAL_LOOP:;

    if (j < 1000)
    {
       if (condition)
       {
           break;
       }

       j++;
       goto MANUAL_LOOP;
    }
}

ถ้าคุณจะไปgotoทำไมไม่gotoออกจากวง?
Willis Blackburn

0

ฉันสังเกตว่าคำถามคือ "มีวิธีอื่นในการทำลายลูปทั้งหมดหรือไม่" ฉันไม่เห็นคุณสมบัติใด ๆ แต่มันไม่ใช่gotoโดยเฉพาะ OP ไม่ได้ขอวิธีที่ดี แล้วเราจะlongjmpออกจากวงในได้อย่างไร? :-)

#include <stdio.h>
#include <setjmp.h>

int main(int argc, char* argv[]) {
  int counter = 0;
  jmp_buf look_ma_no_goto;
  if (!setjmp(look_ma_no_goto)) {
    for (int i = 0; i < 1000; i++) {
      for (int j = 0; j < 1000; j++) {
        if (i == 500 && j == 500) {
          longjmp(look_ma_no_goto, 1);
        }
        counter++;
      }
    }
  }
  printf("counter=%d\n", counter);
}

setjmpฟังก์ชั่นผลตอบแทนครั้งที่สอง ในครั้งแรกจะคืนค่า 0 และโปรแกรมเรียกใช้งานที่ซ้อนกันสำหรับลูป จากนั้นเมื่อทั้งสองiและjเป็น 500 มันจะดำเนินการlongjmpซึ่งทำให้setjmpกลับมาอีกครั้งด้วยค่า 1 โดยข้ามไปที่ลูป

ไม่เพียง แต่จะlongjmpนำคุณออกจากลูปที่ซ้อนกันเท่านั้น แต่ยังทำงานร่วมกับฟังก์ชันที่ซ้อนกันได้อีกด้วย!



-3
for(int i = 0; i < 1000; i++) {
    for(int j = 0; j < 1000; i++) {
       if(condition) {
          func(para1, para2...);
          return;
       }
    }
}

func(para1, para2...) {
    stmt2;
}

โดยพื้นฐานแล้วคุณกำลังบอกว่าควร (1) เรียกใช้ฟังก์ชันพิเศษจำนวนมากจากนั้น (2) หมุนไปตลอดเวลาที่เหลือเมื่อconditionกลายเป็นเท็จ โอ้และลูปที่สองจะทำงานตลอดไปเพราะมันเพิ่มขึ้นiแทนที่จะเป็นj
อ๊ะ

-4
i = 0;

do
{
  for (int j = 0; j < 1000; j++) // by the way, your code uses i++ here!
  {
     if (condition)
     {
       break;
     }
  }

  ++i;

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