스위치 내부에서 루프를 해제하는 방법은 무엇입니까?


113

다음과 같은 코드를 작성하고 있습니다.

while(true) {
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        break; // **HERE, I want to break out of the loop itself**
    }
}

그렇게하는 직접적인 방법이 있습니까?

플래그를 사용할 수 있고 스위치 바로 뒤에 조건부 중단을 두어 루프에서 벗어날 수 있다는 것을 알고 있습니다. C ++에 이미 이것에 대한 구조가 있는지 알고 싶습니다.


20
전환 후 조건부 휴식이 필요한 이유는 무엇입니까? while (true)에서 while (flag)으로 시간을 변경하십시오 ...
Tal Pressman

7
@Dave_Jarvis 나는 이것이 그가하려는 일을 설명하기 위해 여기에 넣은 단순화 된 버전이라고 가정합니다.
Alterlife

6
여러 페이지의 함수를 생성하는 이러한 프로그래머 중 한 명이라면 goto매력적이며 때로는 유일한 탈출구를 찾을 수 있습니다. 코드를 몇 줄에 불과한 작은 함수로 구성하고 각각 하나의 작업을 수행하는 경향이 있다면이 문제에 부딪히지 않을 것입니다. (부수적으로 코드도 읽기가 더 쉬울 것입니다.)
sbi

13
가장 가까운 지하철역까지가는 방법 만 알고 싶은데 금연에 대한 조언을 받고 싶다면.
Michael Krelin-해커

7
@hacker : 글쎄, 만약 당신이 연기 때문에 당신 앞에 지하철 역이 보이지 않는다면, 그 조언은 그렇게 나쁘지 않을 수도 있습니다. :)
sbi

답변:


49

전제

다음 코드는 언어 나 원하는 기능에 관계없이 잘못된 형식으로 간주되어야합니다.

while( true ) {
}

찬반론

while( true )루프는 열악한 형태이다 :

  • while 루프의 묵시적 계약을 끊습니다.
    • while 루프 선언은 유일한 종료 조건을 명시 적으로 명시해야 합니다.
  • 영원히 반복된다는 것을 의미합니다.
    • 종료 절을 이해하려면 루프 내의 코드를 읽어야합니다.
    • 영원히 반복되는 루프는 사용자가 프로그램 내에서 프로그램을 종료하는 것을 방지합니다.
  • 비효율적입니다.
    • "true"확인을 포함하여 여러 루프 종료 조건이 있습니다.
  • 버그가 발생하기 쉽습니다.
    • 각 반복마다 항상 실행되는 코드를 어디에 넣을지 쉽게 결정할 수 없습니다.
  • 불필요하게 복잡한 코드로 이어집니다.
  • 자동 소스 코드 분석.
    • 버그, 프로그램 복잡성 분석, 보안 검사를 찾거나 코드 실행없이 다른 소스 코드 동작을 자동으로 도출하기 위해 초기 차단 조건을 지정하면 알고리즘이 유용한 불변성을 결정하여 자동 소스 코드 분석 메트릭을 개선 할 수 있습니다.
  • 무한 루프.
    • 모든 사람이 항상 while(true)무한이 아닌 for 루프를 사용 하는 경우 루프에 실제로 종료 조건이 없을 때 간결하게 통신 할 수있는 기능을 잃게됩니다. (아마도 이것은 이미 일어 났으므로 요점은 논쟁의 여지가 있습니다.)

"이동"의 대안

다음 코드가 더 나은 형식입니다.

while( isValidState() ) {
  execute();
}

bool isValidState() {
  return msg->state != DONE;
}

장점

플래그가 없습니다. 아니 goto. 예외 없음. 변경하기 쉽습니다. 읽기 쉬운. 쉽게 고칠 수 있습니다. 추가로 코드 :

  1. 루프 자체에서 루프의 워크로드에 대한 지식을 분리합니다.
  2. 코드를 유지하는 누군가가 기능을 쉽게 확장 할 수 있습니다.
  3. 여러 종료 조건을 한 곳에서 할당 할 수 있습니다.
  4. 실행할 코드에서 종료 절을 분리합니다.
  5. 원자력 발전소에 더 안전합니다. ;-)

두 번째 요점이 중요합니다. 코드가 어떻게 작동하는지 모르는 상태에서 누군가가 나에게 메인 루프가 다른 스레드 (또는 프로세스)에 CPU 시간을 허용하도록 요청하면 두 가지 솔루션이 떠 오릅니다.

옵션 1

일시 중지를 쉽게 삽입하십시오.

while( isValidState() ) {
  execute();
  sleep();
}

옵션 # 2

실행 무시 :

void execute() {
  super->execute();
  sleep();
}

이 코드는 포함 된 루프보다 더 간단합니다 (따라서 읽기 쉽습니다) switch. 이 isValidState메서드는 루프가 계속되어야하는지 여부 만 결정해야합니다. 이 방법의 주력이로 추상화한다 execute서브 클래스가 기본 동작을 재정의 할 수 있습니다 방법, (어려운 작업은 임베디드를 사용 switch하고 goto).

Python 예

StackOverflow에 게시 된 다음 답변 (Python 질문에 대한)을 대조하십시오.

  1. 영원히 반복하십시오.
  2. 사용자에게 선택 사항을 입력하도록 요청합니다.
  3. 사용자 입력이 '다시 시작'이면 계속 반복됩니다.
  4. 그렇지 않으면 루프를 영원히 중지하십시오.
  5. 종료.
암호
while True: 
    choice = raw_input('What do you want? ')

    if choice == 'restart':
        continue
    else:
        break

print 'Break!' 

대:

  1. 사용자의 선택을 초기화하십시오.
  2. 사용자의 선택이 '다시 시작'이라는 단어 인 동안 반복합니다.
  3. 사용자에게 선택 사항을 입력하도록 요청합니다.
  4. 종료.
암호
choice = 'restart';

while choice == 'restart': 
    choice = raw_input('What do you want? ')

print 'Break!'

여기 while True에서 오해의 소지가 있고 지나치게 복잡한 코드가 생성됩니다.


45
while(true)해로울 것이라는 생각이 제겐 이상해 보입니다. 실행하기 전에 확인하는 루프가 있고, 나중에 확인하는 루프가 있으며, 중간에 확인하는 루프가 있습니다. 후자는 C 및 C ++에 구문 구조가 없기 때문에 while(true)또는 for(;;). 이것이 잘못되었다고 생각한다면 아직 다른 종류의 루프에 대해 충분히 생각하지 않은 것입니다.
sbi

34
내가 동의하지 않는 이유 : while (true) 및 for (;;;)는 그들이하고있는 일을 바로 앞에 명시하기 때문에 (do {} while (true)와 달리) 혼란스럽지 않습니다. 실제로 임의의 루프 조건을 구문 분석하고 설정된 위치를 찾는 것보다 "break"문을 찾는 것이 더 쉽고 정확성을 증명하는 것이 더 어렵지 않습니다. 코드를 어디에 넣을지에 대한 논쟁은 continue 문이있을 때 다루기 어렵고 if 문에서는 그다지 좋지 않습니다. 효율성 주장은 긍정적으로 어리 석습니다.
David Thornley

23
"while (true)"를 볼 때마다 임의의 종료 조건보다 어렵지 않은 내부 종료 조건이있는 루프를 생각할 수 있습니다. 간단히 말해서, foo가 임의의 방식으로 설정된 부울 변수 인 "while (foo)"루프는 중단이있는 "while (true)"보다 낫지 않습니다. 두 경우 모두 코드를 살펴 봐야합니다. 그나저나 어리석은 이유를 제시하는 것은 (그리고 마이크로 효율성은 어리석은 주장입니다) 당신의 경우에 도움이되지 않습니다.
David Thornley

5
@Dave : 나는 이유를 주었다 : 중간에 조건을 확인하는 루프를 얻는 유일한 방법이다. 고전적인 예 : while(true) {string line; std::getline(is,line); if(!is) break; lines.push_back(line);}물론 이것을 사전 조건화 된 루프로 변환 할 수 있습니다 ( std::getline루프 조건으로 사용). 그러나 이것은 그 자체로 단점이 있습니다 ( line루프 범위 밖의 변수 여야 함). 그리고 만약 내가 그랬다면 나는 물어야 할 것입니다. 왜 우리는 어쨌든 세 개의 루프가 있습니까? 우리는 항상 모든 것을 사전 조건화 된 루프로 변환 할 수 있습니다. 가장 적합한 것을 사용하십시오. 경우에 while(true)맞는, 다음을 사용합니다.
sbi

3
이 답변을 읽는 사람들에게 예의로 질문을 생성 한 상품의 품질에 대해 언급하는 것을 피하고 질문 자체에 대한 답변을 고수합니다. 질문은 다음과 같은 변형이며, 여러 언어의 기능이라고 생각합니다. 루프가 다른 루프 내부에 중첩되어 있습니다. 내부 루프에서 외부 루프를 벗어나려고합니다. 어떻게하나요?
Alex Leibowitz 19

172

사용할 수 있습니다 goto.

while ( ... ) {
   switch( ... ) {
     case ...:
         goto exit_loop;

   }
}
exit_loop: ;

12
그 키워드에 열광하지 마십시오.
Fragsworth

45
사람들이 goto. OP는 플래그를 사용하지 않고 명확하게 언급되었습니다 . OP의 한계를 고려하여 더 나은 방법을 제안 할 수 있습니까? :)
Mehrdad Afshari

18
무의미한 고토 싫어하는 사람들을 보상하기 위해 단지 t를 추천했습니다. 나는 Mehrdad가 여기에서 합리적인 해결책을 제안한 것에 대해 그가 몇 점을 잃을 것이라는 것을 알고 있다고 생각합니다.
Michael Krelin-hacker

6
+1, OPs 제한을 고려할 때도 imho 더 좋은 방법은 없습니다. 플래그는 추악하고 전체 루프에서 논리를 분리하므로 파악하기가 더 어렵습니다.
Johannes Schaub-litb

11
하루가 끝나면 goto 구조가 없으면 모든 언어가 실패합니다. 높은 수준의 언어는 그것을 난독 화하지만, 내부적으로 충분히 자세히 보면 모든 루프를 찾거나 조건이 조건부 및 무조건 분기로 귀결됩니다. 우리는 for, while, until, switch, if 키워드를 사용하여 자신을 속이지 만 결국 모두 GOTO (또는 JMP)를 어떤 방식 으로든 사용합니다. 당신은 자신을 속일 수 있고, 그것을 숨기는 모든 종류의 영리한 방법을 발명하거나, 실용적으로 정직하게 될 수 있고, 적절한 경우, 그리고 드물게 goto를 사용할 수 있습니다.
동기화되지

58

다른 해결책은 키워드 continue를와 함께 사용하는 것입니다 break.

for (;;) {
    switch(msg->state) {
    case MSGTYPE
        // code
        continue; // continue with loop
    case DONE:
        break;
    }
    break;
}

continue문을 사용하여 루프를 계속할 각 케이스 레이블을 완료 하고 문을 사용하여 루프를 break종료해야하는 케이스 레이블을 완료하십시오.

물론이 솔루션은 switch 문 다음에 실행할 추가 코드가없는 경우에만 작동합니다.


9
이것은 실제로 매우 우아하지만 대부분의 개발자가 이것이 어떻게 작동하는지 이해하기 위해 먼저 1 분 동안 살펴보아야한다는 단점이 있습니다. :(
sbi

8
마지막 문장은 이것이 그렇게 훌륭하지 않게 만드는 :(것입니다. 그렇지 않으면 매우 깨끗한 구현입니다.
nawfal

생각해 보면 좋은 컴퓨터 코드는 없습니다. 우리는 그것을 이해하도록 훈련 받았습니다. 예를 들어 xml은 내가 배울 때까지 쓰레기처럼 보였습니다. 이제 쓰레기가 덜 보입니다. for (int x = 0; x <10; ++ x, moveCursor (x, y)) 실제로 내 프로그램에서 논리 오류가 발생했습니다. 마지막에 업데이트 조건이 발생한 것을 잊었습니다.

22

이를위한 깔끔한 방법은 이것을 함수에 넣는 것입니다 :

int yourfunc() {

    while(true) {

        switch(msg->state) {
        case MSGTYPE: // ... 
            break;
        // ... more stuff ...
        case DONE:
            return; 
        }

    }
}

선택적으로 (그러나 '나쁜 사례') : 이미 제안했듯이 goto를 사용하거나 스위치 내부에서 예외를 throw 할 수 있습니다.


4
예외는 잘 알려진 함수의 동작이 아닌 예외를 발생시키는 데 사용되어야합니다.
Clement Herreman

나는 Afterlife에 동의한다 : 그것을 함수에 넣어 라.
dalle

14
이 경우 예외를 던지는 것은 정말 악합니다. 그것은 "고토를 사용하고 싶지만 사용하지 말아야 할 곳을 읽었으므로 속임수로 가서 똑똑한 척 할 것"이라는 의미입니다.
Gorpik

예외를 던지거나 goto를 사용하는 것이 끔찍한 생각이라는 데 동의하지만 작동 옵션이며 내 대답에 나열되었습니다.
Alterlife

2
예외가 발생하면 GOTO 대신 COMEFROM이 대체됩니다. 하지마. 고토가 훨씬 더 잘 작동합니다.
David Thornley

14

AFAIK C ++에는 "이중 브레이크"또는 이와 유사한 구조가 없습니다. 가장 가까운 것은 goto-이름에 나쁜 의미가 있지만 이유 때문에 언어에 존재합니다-신중하고 드물게 사용되는 한 실행 가능한 옵션입니다.


8

스위치를 다음과 같이 별도의 기능에 넣을 수 있습니다.

bool myswitchfunction()
{
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        return false; // **HERE, I want to break out of the loop itself**
    }
    return true;
}

while(myswitchfunction())
    ;

7

goto를 좋아하지 않더라도 루프를 종료하기 위해 예외를 사용하지 마십시오. 다음 샘플은 얼마나 추한지 보여줍니다.

try {
  while ( ... ) {
    switch( ... ) {
      case ...:
        throw 777; // I'm afraid of goto
     }
  }
}
catch ( int )
{
}

답변 goto에서와 같이 사용 합니다. 이 경우 다른 옵션보다 코드가 더 명확 해집니다. 질문이 도움 되기를 바랍니다 .goto

그러나 나는 goto문자열 때문에 사용하는 것이 유일한 옵션 이라고 생각 합니다 while(true). 루프 리팩토링을 고려해야합니다. 다음 솔루션을 가정합니다.

bool end_loop = false;
while ( !end_loop ) {
    switch( msg->state ) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        end_loop = true; break;
    }
}

또는 다음과 같은 경우도 있습니다.

while ( msg->state != DONE ) {
    switch( msg->state ) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
}

1
그래,하지만 예외와 같은 더 적은 ;-)
quamrana

3
예외를 사용하여 goto를 에뮬레이트하는 것은 실제로 goto를 사용하는 것보다 더 나쁩니다! 아마 상당히 느릴 것입니다.
John Carter

2
@Downvoter : 예외를 사용해서는 안된다고 말했거나 리팩토링을 제안했기 때문입니까?
Kirill V. Lyadvinsky

1
반대 투표자는 아니지만 ... 당신의 대답은 루프를 빠져 나가기 위해 예외를 사용하는 아이디어를 제안하거나 비난하는지 여부가 명확하지 않습니다. 아마도 첫 번째 문장은 "좋지 않더라도 루프를 종료하기 위해 예외를 사용 goto하지 마십시오 :"?
Jonathan Leffler

1
@Jonathan Leffler, 감사합니다. 귀중한 댓글 샘플을 보여 주셨습니다. 귀하의 의견을 염두에두고 답변을 업데이트했습니다.
Kirill V. Lyadvinsky

5

이 경우 루프를 벗어나기위한 C ++ 구조가 없습니다.

플래그를 사용하여 루프를 중단하거나 (적절한 경우) 코드를 함수로 추출하고 return.


2

잠재적으로 goto를 사용할 수 있지만 루프를 중지하는 플래그를 설정하는 것이 좋습니다. 그런 다음 스위치에서 나오십시오.


2

while 루프의 조건을 수정하여 문제가 사라지는 이유는 무엇입니까?

while(msg->state != DONE)
{
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        // We can't get here, but for completeness we list it.
        break; // **HERE, I want to break out of the loop itself**
    }
}

2

아니요, "break"키워드가 스위치 블록을 종료하기 위해 이미 예약되어 있으므로 C ++에는 이에 대한 구문이 없습니다. 또는 종료 플래그가있는 do..while ()이면 충분할 수 있습니다.

do { 
    switch(option){
        case 1: ..; break;
        ...
        case n: .. ;break;
        default: flag = false; break;
    }
} while(flag);

1

나는 생각한다.

while(msg->state != mExit) 
{
    switch(msg->state) 
    {
      case MSGTYPE: // ...
         break;
      case DONE:
      //  .. 
      //  ..
      msg->state =mExit;
      break;
    }
}
if (msg->state ==mExit)
     msg->state =DONE;

0

이를 수행하는 가장 간단한 방법은 SWITCH를 수행하기 전에 간단한 IF를 입력하고 IF가 루프를 종료하기위한 조건을 테스트하는 것입니다. .......... 가능한 한 간단합니다.


2
음 ... while 인수에 해당 조건을 입력하면 더 간단해질 수 있습니다. 그게 거기에 있다는 뜻입니다! :)
Mark A. Donohoe

0

breakC ++ 의 키워드는 가장 많이 중첩 된 반복 또는 switch문만 종료합니다 . 따라서 문 while (true)내에서 직접 루프를 벗어날 수 없습니다 switch. 그러나 이러한 유형의 문제에 대한 훌륭한 패턴이라고 생각하는 다음 코드를 사용할 수 있습니다.

for (; msg->state != DONE; msg = next_message()) {
    switch (msg->state) {
    case MSGTYPE:
        //...
        break;

    //...
    }
}

msg->state같을 때 무언가를해야하는 경우 DONE(예 : 정리 루틴 실행) for루프 바로 뒤에 해당 코드를 배치합니다 . 즉, 현재 다음이있는 경우 :

while (true) {
    switch (msg->state) {
    case MSGTYPE:
        //... 
        break;

    //...

    case DONE:
        do_cleanup();
        break;
    }

    if (msg->state == DONE)
        break;

    msg = next_message();
}

그런 다음 대신 사용하십시오.

for (; msg->state != DONE; msg = next_message()) {
    switch (msg->state) {
    case MSGTYPE:
        //...
        break;

    //...
    }
}

assert(msg->state == DONE);
do_cleanup();

0

설명의 깊이를 고려할 때 이것이 얼마나 간단한 지 놀랍습니다 ... 여기에 필요한 모든 것이 있습니다 ...

bool imLoopin = true;

while(imLoopin) {

    switch(msg->state) {

        case MSGTYPE: // ... 
            break;

        // ... more stuff ...

        case DONE:
            imLoopin = false;
            break;

    }

}

LOL !! 정말! 그게 전부입니다! 하나의 추가 변수!


0
while(MyCondition) {
switch(msg->state) {
case MSGTYPE: // ... 
    break;
// ... more stuff ...
case DONE:
   MyCondition=false; // just add this code and you will be out of loop.
    break; // **HERE, you want to break out of the loop itself**
}
}

0

나는 같은 문제를 가지고 깃발을 사용하여 해결했습니다.

bool flag = false;
while(true) {
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        flag = true; // **HERE, I want to break out of the loop itself**
    }
    if(flag) break;
}

-2

C ++ 구문을 잘 기억한다면 breakfor .NET과 마찬가지로 문에 레이블을 추가 할 수 있습니다 goto. 따라서 원하는 것은 쉽게 작성됩니다.

while(true) {
    switch(msg->state) {
    case MSGTYPE: // ...
        break;
    // ... more stuff ...
    case DONE:
        break outofloop; // **HERE, I want to break out of the loop itself**
    }
}

outofloop:
// rest of your code here

1
불행히도 당신의 기억은 결함이 있습니다. 드물게 유용 할 경우 좋은 기능이 될 것입니다. (나는에서 휴식하는 수준의 수에 대한 제안을 본 적이 있지만 버그 같은 나에게 그 모습이 일어날 기다리고.)
데이빗 쏜리

그렇다면 그것은 자바 였음에 틀림 없다. 대답 해줘서 고마워. 그리고 물론 나머지도 당신이 옳습니다.
Andrei Vajna II

-3
  while(true)
  {
    switch(x)
    {
     case 1:
     {
      break;
     }
    break;
   case 2:
    //some code here
   break;
  default:
  //some code here
  }
}

1
해당 코드 샘플의 모든 중단은 switch 문을 벗어납니다.
Dan Hulme 2012 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.