for 루프에서 break를 사용하는 것이 나쁜 습관입니까? [닫은]


123

루프 내에서 break 을 사용하는 것이 나쁜 습관 입니까?for

배열에서 값을 검색하고 있습니다. for 루프 내부와 값이 발견되면 break;for 루프를 종료하기 위해 비교합니다.

이것은 나쁜 습관입니까? 사용 된 대안을 보았습니다. 변수를 정의 vFound하고 값이 발견되면 true로 설정 vFound하고 for문 조건을 확인 합니다. 하지만이 목적을 위해 새 변수를 만들어야합니까?

일반적인 C 또는 C ++ for 루프의 맥락에서 묻습니다.

추신 : MISRA 코딩 지침은 break 사용을 권장합니다.


28
하지 마십시오 break같은 리그에서 goto:)
BoltClock

2
어, 나는 이동과 같은 리그에 배치하지 않았습니다 ... MISRA 규칙은 올바르게 기억하면 휴식을 지정합니다.
kiki

1
루프 내에서 break를 사용하지 않는 것을 생각할 수있는 유일한 이유는 수행중인 작업의 결과를 변경할 수있는 더 많은 항목을 처리해야하는 경우입니다.
Younes

4
MISRA 규칙이 완화되었습니다 : misra.org.uk/forum/viewtopic.php?f=75&t=298
Johan Kotlinski

답변:


122

여기에 많은 답변이 있지만 아직 언급되지 않았습니다.

깔끔하고 쉽게 읽을 수있는 루프를 작성하면 for 루프에서 break또는 사용과 관련된 대부분의 "위험" continue이 무효화됩니다. 루프의 본문이 여러 화면 길이에 걸쳐 있고 중첩 된 하위 블록이 여러 개있는 경우, 일부 코드가 중단 후에 실행되지 않는다는 사실을 쉽게 잊을 수 있습니다. 그러나 루프가 짧고 요점이라면 break 문의 목적이 분명해야합니다.

루프가 너무 커지면 대신 루프 내에서 잘 이름이 지정된 함수 호출을 하나 이상 사용하십시오. 그렇게하지 않는 유일한 이유는 병목 현상을 처리하는 것입니다.


11
사실입니다. 물론 루프가 너무 크고 복잡해서 내부에서 무슨 일이 일어나고 있는지 확인하기 어렵다면 휴식이 있든 없든 문제가됩니다.
Jay

1
또 다른 문제는 중단하고 계속하면 리팩토링에 문제가 발생한다는 것입니다. 이것은 이것이 나쁜 습관이라는 신호일 수 있습니다. 코드의 의도는 if 문을 사용할 때 더 명확합니다.
Ed Greaves

이러한 "잘 명명 된 함수 호출"은 다른 곳에서 사용되지 않는 외부 정의 개인 함수 대신 로컬로 정의 된 람다 함수일 수 있습니다.
DavidRR

135

아니요, 휴식은 올바른 해결책입니다.

부울 변수를 추가하면 코드를 읽기가 더 어려워지고 잠재적 인 오류 소스가 추가됩니다.


2
동의했습니다. 특히 하나 이상의 조건에서 루프를 종료하려는 경우. 루프를 떠나는 부울은 정말 혼란 스러울 수 있습니다.
Rontologist 2010 년

2
내가 사용하는 이유입니다 goto이 없기 때문에 - 레벨 2 + 중첩 루프의 탈옥break(level)
Петър Петров

3
차라리 루프 코드를 함수에 넣고 반환합니다. 이것은 goto를 피하고 코드를 더 작은 덩어리로 나눕니다.
Dave Branton 2017 년

53

'break'문이 포함 된 모든 종류의 전문 코드를 찾을 수 있습니다. 필요할 때마다 이것을 사용하는 것이 완벽하게 합리적입니다. 귀하의 경우이 옵션은 루프에서 나올 목적으로 별도의 변수를 만드는 것보다 낫습니다.


47

루프 break에서뿐만 아니라 사용 continue하는 for것은 완벽합니다.

코드를 단순화하고 가독성을 향상시킵니다.


예 .. 한동안 저는 아이디어가 마음에 들지 않았고 그 주위에 코딩을했습니다.하지만 얼마 지나지 않아 ... "해결 방법"이 유지 관리의 악몽 인 경우가 많았습니다. 악몽은 아니지만 오류가 발생하기 쉽습니다.
MetalMikester 2010 년

24

나쁜 습관과는 거리가 먼 Python (및 다른 언어?)은 for루프 구조를 확장 하여 루프 가 그렇지 않은 경우 에만 일부 가 실행됩니다 . break

for n in range(5):
    for m in range(3):
        if m >= n:
            print('stop!')
            break
        print(m, end=' ')
    else:
        print('finished.')

산출:

stop!
0 stop!
0 1 stop!
0 1 2 finished.
0 1 2 finished.

사용하지 않고 break편리한 동등한 코드 else:

for n in range(5):
    aborted = False
    for m in range(3):
        if not aborted:
            if m >= n:
                print('stop!')
                aborted = True
            else:            
                print(m, end=' ')
    if not aborted:
        print('finished.')

2
Oooh, 나는 그것을 좋아하고 때때로 C에서 그것을 원했다. 좋은 구문도 모호함없이 미래의 C와 같은 언어에 적용될 수있다.
supercat

"언어 C와 같은"P
nirvanaswap

15

break 문을 사용하는 데 본질적으로 잘못된 것은 없지만 중첩 루프는 혼란 스러울 수 있습니다. 가독성을 높이기 위해 많은 언어 ( 적어도 Java는 지원함)가 레이블 분리를 지원하여 가독성을 크게 향상시킵니다.

int[] iArray = new int[]{0,1,2,3,4,5,6,7,8,9};
int[] jArray = new int[]{0,1,2,3,4,5,6,7,8,9};

// label for i loop
iLoop: for (int i = 0; i < iArray.length; i++) {

    // label for j loop
    jLoop: for (int j = 0; j < jArray.length; j++) {

        if(iArray[i] < jArray[j]){
            // break i and j loops
            break iLoop;
        } else if (iArray[i] > jArray[j]){  
            // breaks only j loop
            break jLoop;
        } else {
            // unclear which loop is ending
            // (breaks only the j loop)
            break;
        }
    }
}

break (및 return) 문은 종종 순환 적 복잡성 을 증가시켜 코드가 모든 경우에 올바른 작업을 수행하고 있음을 증명하기 어렵게 만든다고 말할 것입니다.

특정 항목에 대해 시퀀스를 반복하는 동안 중단 사용을 고려하고 있다면 데이터를 보관하는 데 사용되는 데이터 구조를 재고 할 수 있습니다. 세트 또는지도와 같은 것을 사용하면 더 나은 결과를 얻을 수 있습니다.


4
순환 복잡성의 경우 +1.
sp00m

.NET 프로그래머는 복잡한 루프에 대한 잠재적 인 대안 으로 LINQ의 사용을 탐색하고자 할 수 있습니다 for.
DavidRR

14

break 는 사용할 수있는 완전히 허용되는 명령문입니다 ( continue , btw). 코드 가독성에 관한 것입니다. 루프가 너무 복잡하지 않은 한 괜찮습니다.

goto 와 같은 리그가 아니 었습니다 . :)


나는 그것이 break 문 사실상 goto 라고 주장하는 것을 보았다 .
glenatron 2010 년

3
goto의 문제점은 어디든 가리킬 수 있다는 것입니다. 중단과 계속 모두 코드의 모듈성을 유지합니다. 이 인수는 각 함수에 대해 단일 종료점을 갖는 것과 동일한 수준에 있습니다.
Zachary Yates

1
함수에 대해 여러 출구 지점을 갖는 것이 사실상 goto 라고 주장하는 것을 들었습니다 . ; D
glenatron

2
@glenatron goto의 문제는 goto 문이 아니라 goto가 점프하는 레이블의 도입이 문제입니다. goto 문을 읽는 동안 제어 흐름에 대한 불확실성은 없습니다. 레이블을 읽을 때 제어 흐름에 대한 많은 불확실성이 있습니다 (이 레이블로 제어를 전송하는 모든 문제는 어디에 있습니까?). break 문은 레이블을 도입하지 않으므로 새로운 합병증을 도입하지 않습니다.
Stephen C. Steel

1
"휴식"은 블록 만 떠날 수있는 특수 고토입니다. "goto"의 역사적 문제는 (1) 적절한 제어 구조로는 불가능한 방식으로 겹치는 루프 블록을 구성하는 데 사용될 수 있으며 자주 사용되었습니다. (2) 일반적으로 "거짓"이었던 "if-then"구조를 수행하는 일반적인 방법은 실제 케이스를 코드에서 관련없는 위치로 분기 한 다음 다시 분기하는 것입니다. 이것은 드문 경우에 두 개의 분기 비용이 발생하고 일반적인 경우에는 0이지만 코드가 끔찍해 보입니다.
supercat

14

일반 규칙 : 규칙을 따르려면 좀 더 어색하고 읽기 어려운 일을해야 규칙을 어 기고 규칙을 어기십시오.

무언가를 찾을 때까지 반복하는 경우, 밖으로 나올 때 발견 된 것과 발견되지 않은 것을 구별하는 문제에 직면하게됩니다. 그건:

for (int x=0;x<fooCount;++x)
{
  Foo foo=getFooSomehow(x);
  if (foo.bar==42)
    break;
}
// So when we get here, did we find one, or did we fall out the bottom?

좋습니다. 플래그를 설정하거나 "found"값을 null로 초기화 할 수 있습니다. 그러나

그렇기 때문에 일반적으로 검색을 함수로 푸시하는 것을 선호합니다.

Foo findFoo(int wantBar)
{
  for (int x=0;x<fooCount;++x)
  {
    Foo foo=getFooSomehow(x);
    if (foo.bar==wantBar)
      return foo;
  }
  // Not found
  return null;
}

이것은 또한 코드를 정리하는 데 도움이됩니다. 메인 라인에서 "find"는 단일 문이되고 조건이 복잡하면 한 번만 작성됩니다.


1
내가 말하고 싶었던 그대로. 종종를 사용 break하고 있음을 발견하면 코드를 함수로 리팩토링하여 return대신 사용할 수있는 방법을 찾으려고 합니다.
Rene Saarsoo

나는 당신에게 100 % 동의하며, 휴식을 사용하는 데 본질적으로 잘못된 것은 없습니다. 그러나 일반적으로 포함 된 코드를 break가 return으로 대체되는 함수로 추출해야 할 수도 있음을 나타냅니다. 이는 명백한 실수 라기보다는 '코드 냄새'로 간주되어야합니다.
vascowhite

귀하의 답변은 여러 return 문 을 사용하는 것이 좋습니다.
DavidRR

13

언어에 따라 다릅니다. 여기서 부울 변수를 확인할 수 있습니다.

for (int i = 0; i < 100 && stayInLoop; i++) { ... }

배열을 반복 할 때는 할 수 없습니다.

for element in bigList: ...

어쨌든 break두 코드를 더 읽기 쉽게 만들 것입니다.


7

귀하의 예에서는 for 루프 의 반복 횟수를 모릅니다 . 대신 while 루프를 사용 하면 처음에 반복 횟수를 결정할 수 없습니다.

따라서 루프가 while 루프 로 더 잘 설명 될 수 있으므로 일반적으로 break statemement 를 사용할 필요가 없습니다 .


1
"for"루프는 알려진 최대 반복 횟수가있는 경우 종종 좋습니다. 즉, while (1) 루프는 항목을 검색하고 존재하지 않는 경우 만들 계획 인 시나리오에서 때때로 더 좋습니다. 이 경우 코드가 항목을 찾으면 직접 중단하고 배열의 끝에 도달하면 새 항목을 만든 다음 중단합니다.
supercat 2010 년

2
주어진 예에서 배열을 통해 키를 검색하면 for 루프가 적절한 관용적 구조입니다. 배열을 살펴보기 위해 while 루프를 사용하지 않습니다. for 루프를 사용합니다. (또는 가능한 경우 for-each 루프). 다른 프로그래머가 "for (int i = 0; i <ra.length; i ++) {}"구문을 "Walk through the array"로 즉시 인식하므로 다른 프로그래머에 대한 예의로이 작업을 수행합니다. 중단을 추가하면이 구문에 단순히 "가능하면 일찍 종료하십시오"라는 문구입니다.
Chris Cudmore

7

을 (를) 사용하도록 권장하는 다른 사용자의 의견에 동의합니다 break. 명백한 결과적 질문은 왜 누군가가 다른 방법을 추천할까요? 음 ... break를 사용하면 블록의 나머지 코드와 나머지 반복을 건너 뜁니다. 때때로 이로 인해 버그가 발생합니다. 예를 들면 다음과 같습니다.

  • 블록의 맨 위에서 획득 한 리소스는 맨 아래에서 해제 될 수 있지만 ( for루프 내부의 블록에서도 마찬가지 임) 해당 해제 단계는 break명령문 ( "현대"에서 " 조기"종료가 발생하는 경우 실수로 건너 뛸 수 있음) C ++, "RAII"는 신뢰할 수 있고 예외에 안전한 방식으로이를 처리하는 데 사용됩니다. 기본적으로 개체 소멸자는 범위가 어떻게 종료 되든 안정적으로 리소스를 해제합니다.)

  • 누군가 for가 다른 지역화되지 않은 종료 조건이 있음을 알지 못하고 문 에서 조건부 테스트를 변경할 수 있습니다.

  • ndim의 대답에 따르면 일부 사람들은 break비교적 일관된 루프 런타임을 유지하기 위해 s를 피할 수 있지만 break유지되지 않는 부울 조기 종료 제어 변수의 사용과 비교했습니다.

때때로 이러한 버그를 관찰하는 사람들은이 "중단 없음"규칙에 의해 예방 / 완화 될 수 있음을 깨닫습니다. 실제로 "구조화 된 프로그래밍"이라고하는 "안전한"프로그래밍에 대한 전체 관련 전략이 있습니다. 단일 진입 점과 출구 지점도 있습니다 (예 : 이동 없음, 조기 복귀 없음). 일부 버그를 제거 할 수 있지만 의심 할 여지없이 다른 버그가 발생합니다. 왜 그렇게합니까?

  • 그들은 특정 스타일의 프로그래밍 / 코드를 장려하는 개발 프레임 워크를 가지고 있으며, 이것이 제한된 프레임 워크에서 순이익을 생성한다는 통계적 증거를 가지고 있습니다.
  • 이러한 프레임 워크 내에서 프로그래밍 지침 또는 경험의 영향을 받았거나
  • 그들은 단지 독재적인 바보이거나
  • 위의 + 역사적 관성 중 하나 (정확성이 현대 C ++보다 C에 더 적용된다는 점에서 관련됨).

음, 예,하지만 종교적으로 피하려고 노력한 사람들이 코드에서 버그를 발견했습니다 break. 그들은 그것을 피했지만 휴식의 사용을 대체하는 조건을 생각하지 못했습니다.
Tomáš Zato-Monica 복원

5

break다른 사람들이 지적했듯이 사용하는 것은 완벽하게 유효합니다 goto.

vFound루프 외부에서 값이 배열에서 발견되었는지 확인하려는 경우 변수 를 사용할 수 있습니다 . 또한 유지 관리의 관점에서 종료 기준을 알리는 공통 플래그를 갖는 것이 유용 할 수 있습니다.


5

현재 작업중인 코드베이스 (JavaScript 40,000 줄)에 대한 분석을 수행했습니다.

다음 중 22 개의 break진술 만 찾았습니다 .

  • 19 개가 내부 switch문 에 사용 되었습니다 (총 3 개의 switch 문만 있습니다!).
  • 2는 for루프 내부에서 사용 되었습니다 return. 즉, 별도의 함수로 리팩토링되고 문으로 대체되는 것으로 즉시 분류 한 코드입니다 .
  • 마지막 break내부 while루프는 ... git blame누가이 쓰레기를 썼는지 보러 달려 갔어요!

그래서 내 통계에 따르면 : 외부에서 사용 되면 코드 냄새입니다.breakswitch

나는 또한 continue진술을 검색했다 . 아무것도 찾지 못했습니다.


4

그 시점에서 STOP 처리를 완료하고 싶은 것이 잘못된 관행 인 이유를 모르겠습니다.


4

임베디드 세계에는 다음 구조를 사용하는 코드가 많이 있습니다.

    while(1)
    { 
         if (RCIF)
           gx();
         if (command_received == command_we_are_waiting_on)
           break;
         else if ((num_attempts > MAX_ATTEMPTS) || (TickGet() - BaseTick > MAX_TIMEOUT))
           return ERROR;
         num_attempts++;
    }
    if (call_some_bool_returning_function())
      return TRUE;
    else
      return FALSE;

이것은 매우 일반적인 예입니다. 커튼 뒤에서 많은 일이 일어나고 있습니다. 특히 인터럽트가 발생합니다. 이것을 상용구 코드로 사용하지 마십시오. 저는 단지 예제를 설명하려고합니다.

내 개인적인 의견은 루프에 무기한 남아 있지 않도록 적절한주의를 기울이는 한 이런 방식으로 루프를 작성하는 데 아무런 문제가 없다는 것입니다.


1
그것에 대해 자세히 설명해 주시겠습니까? continue응용 프로그램 논리 내부 에 명령문 이 없으면 루프가 전혀 아무것도하지 않는 것처럼 보입니다 . 거기 있어요?
Rene Saarsoo

1
이미 무한 루프에 묶여 있으므로 continue 문은 필요하지 않습니다. 기본적으로 목표를 달성하는 데 필요한 논리를 수행하고 해당 목표가 ​​n 번의 시도에서 충족되지 않거나 시간 초과 조건이 만료되면 중단 구조를 통해 루프를 종료하고 수정 조치를 취하거나 호출 코드에 오류를 알립니다. 공통 버스 (RS-485 등)를 통해 통신하는 uC에서 이러한 유형의 코드를 자주 볼 수 있습니다. 기본적으로 회선을 점유하고 충돌없이 통신하려고합니다. 충돌이 발생하면 임의의 숫자로 결정된 기간을 기다렸다가 시도합니다. 다시.
Nate

1
반대로, 일이 잘되면 조건이 충족되면 break 문을 사용하여 종료합니다. 이 경우 루프 뒤의 코드가 실행되고 모두가 행복합니다.
Nate

"(call_some_bool_returning_function ())이 TRUE를 반환하고 그렇지 않으면 FALSE를 반환합니다." 그냥 "반환 call_some_bool_returning_function ()"이 될 수
frankster

3

사용 사례에 따라 다릅니다. for 루프의 런타임이 일정해야하는 애플리케이션이 있습니다 (예 : 일부 타이밍 제약 조건을 충족하거나 타이밍 기반 공격으로부터 데이터 내부를 숨기기 위해).

이러한 경우 플래그를 설정하고 모든 for루프 반복이 실제로 실행 된 후에 만 플래그 값을 확인하는 것이 합리적 입니다. 물론 모든 for 루프 반복은 거의 동일한 시간이 걸리는 코드를 실행해야합니다.

당신은 런타임 ... 사용에 대해 걱정하지 않는 경우 break;continue;코드를 읽기 쉽게하기 위해.


1
타이밍이 중요한 경우 프로그램이 쓸모없는 작업을 수행하도록하는 것보다 약간의 수면을 취하고 시스템 리소스를 절약하는 것이 더 나을 수 있습니다.
Bgs

2

MISRA C dev에 내 기업에서 사용되는 98 규칙, break 문은 사용하지 않아야한다 ...

편집 : MISRA '04에서 중단이 허용됩니다.


2
내가이 질문을 한 정확한 이유! 나는 그러한 규칙이 코딩 지침으로 존재하는 이유를 찾지 못한다. 또한 여기에있는 모든 사람들이 말했듯이 휴식을 취하십시오. 더 좋아 보인다.
kiki

1
나는 그것이 왜 금지되어 있는지 정확히 알지 못합니다 (나에 대해 생각하는 것보다 더 영리한 사람 ...)하지만 휴식 (그리고 다중 반환)은 가독성을 위해 더 좋습니다 (내 의견으로는, 내 의견은 기업 규칙이 아닙니다 ... )
Benoît

1
어, MISRA 규칙은 break 문 사용을 허용합니다 : misra.org.uk/forum/viewtopic.php?f=75&t=298
luis.espinal

우리는 MISRA 98 규칙을 사용했습니다. MISRA 2004가 규칙을 변경하는 것은 정확합니다
Benoît

0

물론 break;for 루프 또는 foreach 루프를 중지하는 솔루션입니다. foreach 및 for 루프의 PHP에서 사용하고 작동하는 것으로 나타났습니다.


0

동의하지 않습니다!

for 루프의 기본 제공 기능을 무시하여 자신 만의 것으로 만드는 이유는 무엇입니까? 바퀴를 재발 명 할 필요가 없습니다.

나는 당신의 수표를 for 루프의 맨 위에있는 것이 더 합리적이라고 생각합니다.

for(int i = 0; i < myCollection.Length && myCollection[i].SomeValue != "Break Condition"; i++)
{
//loop body
}

또는 행을 먼저 처리해야하는 경우

for(int i = 0; i < myCollection.Length && (i == 0 ? true : myCollection[i-1].SomeValue != "Break Condition"); i++)
{
//loop body
}

이렇게하면 모든 것을 수행하고 훨씬 더 깔끔한 코드를 만드는 함수를 작성할 수 있습니다.

for(int i = 0; i < myCollection.Length && (i == 0 ? true : myCollection[i-1].SomeValue != "Break Condition"); i++)
{
    DoAllThatCrazyStuff(myCollection[i]);
}

또는 조건이 복잡하다면 해당 코드도 옮길 수 있습니다!

for(int i = 0; i < myCollection.Length && BreakFunctionCheck(i, myCollection); i++)
{
    DoAllThatCrazyStuff(myCollection[i]);
}

휴식으로 가득 찬 "프로페셔널 코드"는 나에게 전문 코드처럼 들리지 않습니다. 게으른 코딩처럼 들립니다.)


4
코딩 게으른 : 전문적인 코드를입니다
제이슨

유지하는 것이 엉덩이에 통증이 아닌 경우에만 :)
Biff MaGriff

2
나는 "GTFO ASAP"를 연습하는 경향이 있습니다. 즉, 구조를 깨끗하게 종료 할 수 있다면 가능한 한 빨리 종료를 지정하는 조건에 가깝게해야합니다.
Chris Cudmore

그것도 잘 작동합니다. 내가 이해하려는 주장은 for 루프를 사용하는 경우 내장 된 기능을 사용하여 코드를 읽기 쉽고 모듈화하는 것이 나쁘지 않다는 것입니다. 루프 내부에 break 문이 반드시 있어야한다면 왜 그 수준의 복잡성이 필요한지 생각해 보겠습니다.
Biff MaGriff

1
ㅋ. 낙담하는 대부분의 사람들이 break코드 가독성을위한 것이라고 주장한다는 사실을 고려하십시오 . 이제 위의 루프에서 5 마일 길이의 미친 상태를 다시보십시오 ...
Tomáš Zato-Monica 복원
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.