여러 개의 중첩 된 'for'루프를 종료하기 위해 break를 사용할 수 있습니까?


305

break함수 를 사용하여 여러 개의 중첩 for루프 를 종료 할 수 있습니까?

그렇다면 어떻게 하시겠습니까? break출구의 루프 수를 제어 할 수 있습니까 ?


1
break 또는 goto를 사용하여 여러 개의 중첩 루프를 종료하는 대신 특정 논리를 함수로 묶고 return을 사용하여 여러 개의 중첩 루프를 종료 할 수 있습니다. 이것은 코드의 미학을 유지하고 나쁜 프로그래밍 관행 인 goto를 사용하지 못하게합니다.
Rishab Shinghal

답변:


239

AFAIK, C ++는 Java 및 기타 언어처럼 이름 지정 루프를 지원하지 않습니다. goto를 사용하거나 사용하는 플래그 값을 만들 수 있습니다. 각 루프의 끝에서 플래그 값을 확인하십시오. true로 설정되면 해당 반복에서 벗어날 수 있습니다.


317
goto그것이 최선의 선택이라면 if 를 사용하는 것을 두려워하지 마십시오 .
jkeys

18
나는 새로운 C ++ 프로그래머 (그리고 공식적인 프로그래밍 교육이없는 사람)이므로 goto에 대한 사람들의 고문에 대해 읽은 후. 내 프로그램이 갑자기 폭발하여 나를 죽일지도 모른다는 두려움으로 사용하는 것을 주저하고 있습니다. 그 외에, 내 ti-83에 프로그램을 작성하는 데 사용했을 때 (물론 지루한 수학 수업에서) 기본 편집기가 제공하는 기능에는 goto의 사용이 필요했습니다.
Faken

26
@Faken : goto불량 프로그래머와 실용 프로그래머 의 두 가지 유형의 프로그래머가 사용 합니다. 전자는 자명하다. 만약 당신이 그것들을 잘 사용하기로 선택한다면 후자는 "악"이라는 개념이 더 적을 때 소위 "악"개념을 사용합니다. 때때로 사용해야하는 일부 C ++ 개념 (매크로, goto 's, 전 처리기, 배열)에 대한 이해를 돕기 위해이 내용을 읽으십시오. parashift.com/c++-faq-lite/big-picture.html#faq-6.15
jkeys

41
@Faken : goto를 사용하는 데 아무런 문제가 없습니다. 그건 오용 골칫거리 인 고토.
Everyone

28
@Hooked : goto드물게 사용 하는 것이 최선의 선택 임을 제외하고는 맞습니다 . 루프를 자체 기능 ( inline속도가 걱정되는 경우)에 넣지 마십시오 return.
sbi

265

아니요,로 망치지 마십시오 break. 사용에 대한 마지막 남은 요새입니다 goto.


19
MISRA C ++ 코딩 표준에서는 goto를 사용하여 이러한 정확한 상황을 처리 할 수 ​​있습니다.
Richard Corden

11
실제 프로그래머는 goto를 사용하는 것을 두려워하지 않습니다. 후회없이 25 년 동안이 작업을 수행하면 많은 개발 시간이 절약되었습니다.
Doug Null

1
나는 동의한다. 8K 픽셀이없고 30 개의 Windows OS 호출 시퀀스를 호출해야하는 경우 gotos는 필수입니다.
Michaël Roy

또는 코드를 함수에 넣어서 간단한 "반환".
Tara

68

람다를 사용하여 명시 적 답변을 추가하려면 다음을 수행하십시오.

  for (int i = 0; i < n1; ++i) {
    [&] {
      for (int j = 0; j < n2; ++j) {
        for (int k = 0; k < n3; ++k) {
          return; // yay we're breaking out of 2 loops here
        }
      }
    }();
  }

물론이 패턴에는 특정 제한이 있으며 분명히 C ++ 11 만 있지만 매우 유용하다고 생각합니다.


7
이것은 독자가 리턴이 람다 자체가 아닌 람다가 리턴하는 함수를 야기한다고 생각하는 것을 혼동 할 수 있습니다.
Xunie

3
@ Xunie : 루프가 너무 복잡하여 람다에 있다는 것을 잊어 버린 경우 다른 함수에 넣을 때가되었지만 간단한 경우에는 꽤 잘 작동합니다.
MikeMB

17
이 솔루션은 아름답다고 생각합니다
tuket

람다를 RIIA 템플릿으로 캡처하여 이름을 지정하고 dtor (일명 scope gaurd)에서 호출합니다. 이렇게하면 성능이 향상되지 않지만 가독성이 향상되고 함수 호출 괄호가 누락 될 위험이 줄어 듭니다.
Red.Wave

56

중첩 루프를 해제하는 또 다른 방법은 두 루프를 별도의 함수로 분리 return하고 종료하려는 경우 해당 함수에서 분리 하는 것입니다.

물론 이것은 return끝 이외의 다른 곳에서 함수에서 명시 적으로 해야하는지 여부에 대한 또 다른 논쟁을 불러옵니다 .


7
그것은 C 문제입니다. RIAA를 사용하면 조기 반환과 관련된 모든 문제가 올바르게 처리되므로 조기 반환은 문제가되지 않습니다.
Martin York

4
RIAA를 올바르게 적용하면 C ++의 리소스 정리 문제를 해결할 수 있지만 다른 환경과 언어에서는 조기 반환에 대한 철학적 주장이 계속되고 있음을 알고 있습니다. 코딩 표준 금지 초기 리턴이 함수 continue_processing에서 코드 블록의 실행을 제어하는 부울 변수 (예 :와 같은 )로 흩어진 함수가있는 곳에서 작업 한 시스템 .
Greg Hewgill

21
RIAA 란 무엇입니까? RAII와 같은 것이 있습니까? = D
jkeys

1
그가 가지고있는 for 루프의 수와 둥지의 깊이에 따라 ... 파란 알약이나 빨간 알약을 원하십니까?
Matt

34

break 는 그것을 포함하는 가장 안쪽의 루프 만 빠져 나옵니다.

goto 를 사용 하여 여러 루프에서 벗어날 수 있습니다 .

물론 goto 는 종종 유해한 것으로 간주 됩니다.

브레이크 기능 [...]을 사용하는 것이 적절합니까?

break와 goto를 사용하면 프로그램의 정확성에 대해 추론하기가 더 어려워 질 수 있습니다. 이에 대한 토론은 여기를 참조하십시오 : Dijkstra는 미쳤다 .


16
"고토는 해롭다"밈은보다 일반적인 "제어 흐름 중단은 해롭다"라는 진술과 밀접하게 관련되어 있다는 점에서 좋은 대답이다. "goto is harmony"라고 말한 다음 돌아 서서 breakor를 사용하는 것이 좋습니다 return.
Pavel Minaev

6
@Pavel : breakreturn를 통해 장점을 가지고 goto당신은 그들이 이동하는 위치를 찾기 위해 레이블을 위해 사냥을 할 필요가 없습니다. 예, 그 아래에는 일종의 goto매우 제한적인 것이 있습니다. 그것들은 프로그래머의 패턴 매칭 두뇌에 의해 무제한보다 해독하기가 훨씬 쉽습니다 goto. 따라서 IMO가 바람직합니다.
sbi

@ sbi : 사실이지만 중단은 여전히 ​​구조적 프로그래밍의 일부가 아닙니다. 그것은보다 견딜 수 있습니다 goto.
jkeys

2
@KarlVoigtland Dijkstra 링크가 오래되었습니다. 이 일 것으로 보인다 : blog.plover.com/2009/07
아론 Brager에게

3
이 상황에서 goto를 사용하는 데 아무런 문제가 없습니다. 잘 배치 된 goto는 달리 제안 된 많은 왜곡 된 솔루션보다 뛰어나고 읽기 쉽습니다.
제임스

22

이 answear가 이미 제시되었지만 좋은 접근 방식은 다음을 수행하는 것입니다.

for(unsigned int z = 0; z < z_max; z++)
{
    bool gotoMainLoop = false;
    for(unsigned int y = 0; y < y_max && !gotoMainLoop; y++)
    {
        for(unsigned int x = 0; x < x_max && !gotoMainLoop; x++)
        {
                          //do your stuff
                          if(condition)
                            gotoMainLoop = true;
        }
    }

}

5
좋은하지만 여전히 읽을 수없는, 나는이 경우 goto를 선호합니다
Петър Петров

2
gotoMainLoop매 사이클마다 점검 되기 때문에 코드가 "quite"느려집니다
Thomas

1
이 경우 실제를 goto사용하면 코어를 더 읽기 쉽고 더 나은 성능으로 만들 수 있습니다.
Петър Петров

20

이건 어때요?

for(unsigned int i=0; i < 50; i++)
{
    for(unsigned int j=0; j < 50; j++)
    {
        for(unsigned int k=0; k < 50; k++)
        {
            //Some statement
            if (condition)
            {
                j=50;
                k=50;
            }
        }
    }
}

2
흥미로운 접근법이지만 ered @ inf.ig.sh가 처리하는 방식을 확실히 좋아합니다. for (부호없는 int y = 0; y <y_max &&! gotoMainLoop; y ++).
Andy

15

goto중첩 루프에서 벗어나는 레이블을 사용하는 코드 예제 :

for (;;)
  for (;;)
    goto theEnd;
theEnd:

11

여러 개의 중첩 루프를 제거하는 좋은 방법 중 하나는 코드를 함수로 리팩터링하는 것입니다.

void foo()
{
    for(unsigned int i=0; i < 50; i++)
    {
        for(unsigned int j=0; j < 50; j++)
        {
            for(unsigned int k=0; k < 50; k++)
            {
                // If condition is true
                return;
            }
        }
    }
}

4
...이 함수를 스택 프레임 화하기 위해 10-20 개의 변수를 전달 해야하는 경우 옵션이 아닙니다.
Петър Петров

1
@ ПетърПетров 그런 다음 람다를 사용하면 필요한 곳에 정확하게 정의 할 수 있기 때문에 더 좋습니다.
DarioP

람다의 경우 +1이지만 하나의 스택 프레임이라도 여전히 병목 현상이 발생하는 게임 엔진 코어에서 점검됩니다. 유감
스럽게도

@ ПетърПетров 그런 다음 함수 쌍을 클래스로 변경하고 스택 변수를 개인 멤버로 변경하십시오.
Arthur Tacca

이것은 이미 복잡한 코드를 지나치게 복잡하게 만듭니다. :) 때로는 goto가 유일한 솔루션입니다. 또는 "goto state X"문서를 사용하여 복잡한 오토마타를 작성하는 경우 goto는 실제로 문서에 작성된대로 코드를 읽습니다. 또한 C #과 go는 모두 목적을 가지고 있습니다 .goto가 없으면 언어가 완벽하지 않으며 gotos는 종종 중간 번역기 또는 어셈블리와 유사한 코드를 작성하는 데 가장 많이 사용되는 도구입니다.
Петър Петров

5

goto는 중첩 루프를 끊는 데 매우 도움이 될 수 있습니다

for (i = 0; i < 1000; i++) {
    for (j = 0; j < 1000; j++) {
        for (k = 0; k < 1000; k++) {
             for (l = 0; l < 1000; l++){
                ....
                if (condition)
                    goto break_me_here;
                ....
            }
        }
    }
}

break_me_here:
// Statements to be executed after code breaks at if condition

3

break문은 가장 가까운 바깥 쪽의 실행 종료 do, for, switch, 또는 while이 나타나는 문을. 제어는 종료 된 명령문 다음에 나오는 명령문으로 전달됩니다.

에서 MSDN .


3

goto이 상황에서 a 가 유효 하다고 생각합니다 .

break/ 를 시뮬레이트하려면 continue다음을 원합니다.

단절

for ( ;  ;  ) {
    for ( ;  ;  ) {
        /*Code here*/
        if (condition) {
            goto theEnd;
        }
    }
}
theEnd:

계속하다

for ( ;  ; ) {
    for ( ;  ;  ) {
        /*Code here*/
        if (condition) {
            i++;
            goto multiCont;
        }
    }
    multiCont:
}

첫 번째 루프 의 반복 표현 이 실행되지 않기 때문에 "계속"은 작동 하지 않습니다.
Fabio A.

첫 번째 루프의 반복자가이라고 가정합니다 i. 따라서 이동 i++하기 전에
JuliusAlphonso

0

PHP와 같은 다른 언어는 break (즉, break 2;)에 대한 매개 변수를 허용하여 중단하려는 중첩 루프 수준을 지정하지만 C ++에서는 그렇지 않습니다. 루프 전에 false로 설정 한 부울을 사용하고, 중단하려는 경우 루프에서 true로 설정 한 부울을 사용하고, 중첩 된 루프 다음에 조건부 나누기, 부울이 true로 설정되었는지 확인하여이를 해결해야합니다. 그렇다면 예.


0

나는 이것이 오래된 포스트라는 것을 안다. 그러나 나는 조금 논리적이고 간단한 대답을 제안 할 것입니다.

for(unsigned int i=0; i < 50; i++)
    {
        for(unsigned int j=0; j < conditionj; j++)
        {
            for(unsigned int k=0; k< conditionk ; k++)
            {
                // If condition is true

                j= conditionj;
               break;
            }
        }
    }

3
j = conditionj대신 복잡한 술어가있는 경우 작동하지 않으므로 확장 성이 떨어지는 솔루션 은 아닙니다 j < conditionj.
Sergey

0

하나의 bool변수로 여러 루프를 분리 하십시오 (아래 참조).

bool check = true;

for (unsigned int i = 0; i < 50; i++)
{
    for (unsigned int j = 0; j < 50; j++)
    {
        for (unsigned int k = 0; k < 50; k++)
        {
            //Some statement
            if (condition)
            {
                check = false;
                break;
            }
        }
        if (!check)
        {
            break;
        }
    }
    if (!check)
    {
        break;
    }
}

이 코드에서 우리는 break;모든 루프입니다.


0

그만한 가치가 있는지 확실하지 않지만 몇 가지 간단한 매크로를 사용하여 Java의 명명 된 루프를 에뮬레이션 할 수 있습니다.

#define LOOP_NAME(name) \
    if ([[maybe_unused]] constexpr bool _namedloop_InvalidBreakOrContinue = false) \
    { \
        [[maybe_unused]] CAT(_namedloop_break_,name): break; \
        [[maybe_unused]] CAT(_namedloop_continue_,name): continue; \
    } \
    else

#define BREAK(name) goto CAT(_namedloop_break_,name)
#define CONTINUE(name) goto CAT(_namedloop_continue_,name)

#define CAT(x,y) CAT_(x,y)
#define CAT_(x,y) x##y

사용법 예 :

#include <iostream>

int main()
{
    // Prints:
    // 0 0
    // 0 1
    // 0 2
    // 1 0
    // 1 1

    for (int i = 0; i < 3; i++) LOOP_NAME(foo)
    {
        for (int j = 0; j < 3; j++)
        {
            std::cout << i << ' ' << j << '\n';
            if (i == 1 && j == 1)
                BREAK(foo);
        }
    }
}

다른 예시:

#include <iostream>

int main()
{
    // Prints: 
    // 0
    // 1
    // 0
    // 1
    // 0
    // 1

    int count = 3;
    do LOOP_NAME(foo)
    {
        for (int j = 0; j < 3; j++)
        {
            std::cout << ' ' << j << '\n';
            if (j == 1)
                CONTINUE(foo);
        }
    }
    while(count-- > 1);
}

-1
  while (i<n) {
    bool shouldBreakOuter = false;
    for (int j=i + 1; j<n; ++j) {
      if (someCondition) {
          shouldBreakOuter = true;
      }
    }

    if (shouldBreakOuter == true)
      break;

  }

-3

try ... catch를 사용할 수 있습니다.

try {
    for(int i=0; i<10; ++i) {
        for(int j=0; j<10; ++j) {
            if(i*j == 42)
                throw 0; // this is something like "break 2"
        }
    }
}
catch(int e) {} // just do nothing
// just continue with other code

한 번에 여러 루프에서 벗어나야하는 경우 종종 예외입니다.


1
이 답변이 많은 투표권을 얻는 이유를 알고 싶습니다.
hkBattousai 2016 년

6
@hkBattousai 솔루션이 예외를 사용하여 실행 흐름을 제어하기 때문에 다운 투표권이 있습니다. 이름에서 알 수 있듯이 예외는 예외적 인 경우에만 사용해야합니다.
Helio Santos

4
@HelioSantos 언어가 적절한 솔루션을 제공하지 않는 예외적 인 상황이 아닙니까?
hkBattousai 2016 년

8
예외는 느리다.
Gordon

2
던지기의 성능 영향은 99 %의 복구 할 수없는 오류가 아닌 무언가에 막대합니다.
Michaël Roy

-4

for-loop의 의미는 일반적으로 지정된 횟수만큼 실행됨을 나타 내기 때문에 for-loop를 해제하는 것은 조금 이상합니다. 그러나 모든 경우에 나쁘지는 않습니다. 컬렉션에서 무언가를 검색하고 그것을 찾은 후에 깨고 싶다면 유용합니다. 그러나 C ++에서는 중첩 루프를 해제 할 수 없습니다. 레이블이 지정된 구분을 사용하여 다른 언어로되어 있습니다. 라벨과 고토를 사용할 수 있지만 밤에 가슴 앓이를 줄 수 있습니다. 그래도 가장 좋은 옵션 인 것 같습니다.


11
전혀 이상하지 않습니다. 무언가를 찾기 위해 컬렉션을 반복하고 (더 빠른 검색 방법이 없다면) 루프를 마무리 할 필요가 없습니다. (예를 들어)
Joe
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.