중첩 루프에서 벗어나기


216

다른 루프에 중첩 된 for 루프가있는 경우 가능한 빨리 두 루프 (내부 및 외부)에서 효율적으로 나올 수있는 방법은 무엇입니까?

부울을 사용하고 싶지 않다가 다른 방법으로 가야한다고 말하고 외부 루프 후에 첫 번째 코드 줄을 실행해야합니다.

이것에 대해 빠르고 좋은 방법은 무엇입니까?

예외는 저렴하지 않고 진정으로 예외적 인 조건에서만 발생해야한다고 생각했습니다. 따라서이 솔루션이 성능 측면에서 좋을 것이라고 생각하지 않습니다.

.NET의 새로운 기능 (비정형 방법)을 사용하여 매우 근본적인 것을 수행하는 것이 옳다고 생각하지 않습니다.


방금 확인하고 싶었던 이유는 무엇입니까?
Jon Limjap

3
부울을 사용하고 싶지 않은 이유는 무엇입니까? 그게 뭐가 문제 야?
Anthony

VB.net에서는 임의의 수의 루프 주위에 try / finally 문 (캐치 없음)을 래핑 할 수 있으며 "exit try"는 모든 지점에서 종료됩니다.
Brain2000

답변:


206

글쎄, goto그러나 그것은 추악하고 항상 가능한 것은 아닙니다. 루프를 메소드 (또는 anon-method)에 배치하고 return메인 코드로 돌아가는 데 사용할 수도 있습니다.

    // goto
    for (int i = 0; i < 100; i++)
    {
        for (int j = 0; j < 100; j++)
        {
            goto Foo; // yeuck!
        }
    }
Foo:
    Console.WriteLine("Hi");

vs :

// anon-method
Action work = delegate
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits anon-method
        }
    }
};
work(); // execute anon-method
Console.WriteLine("Hi");

C # 7에서는 "로컬 함수"를 가져와야합니다 (구문 tbd 등).

// local function (declared **inside** another method)
void Work()
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits local function
        }
    }
};
Work(); // execute local function
Console.WriteLine("Hi");

49
이 유형의 상황에서 goto를 사용하는 것이 break와 같은 일반적인 사용보다 더 나쁘지 않다고 생각합니다 (모두 레이블에 대한 무조건 분기이므로 레이블이 암시 적입니다).
Greg Beech

37
때때로 goto는 대안보다 덜 악하다
seanb

7
@BeowulfOF-break는 내부 및 외부 루프가 아닌 내부 루프에서만 분리됩니다.
Greg Beech

36
Goto 자체는 추악하지 않습니다. 추악한 것은 goto를 학대하여 스파게티 코드를 만드는 것입니다. 중첩 루프에서 벗어나기 위해 goto를 사용하는 것은 완벽합니다. 게다가, 구조적 프로그래밍 관점에서 볼 때 모든 중단, 계속 및 복귀는 고토보다 낫지 않습니다. 기본적으로 그것들은 더 나은 패키징에서 동일합니다. 그렇기 때문에 순수한 파스칼과 같은 순수한 구조 언어에는 세 가지가 모두 부족합니다.
el.pescado

11
@ el.pescado는 완전히 옳습니다. 많은 코더들이 그 자체goto 로 해롭다 믿음으로 오해를 받아 왔지만 , 단순히 어떤 일을하는 것은 올바른 도구이며 많은 악기들이 오용 될 수 있습니다. 이 종교 혐오에 대한이 goto솔직히 아주 바보입니다 확실히 해제 과학.
o0 '.

96

C #에서 자주 사용되는 접근 방식의 C-루프 조건 외부의 외부 루프 변수 값 설정 (즉, int 변수를 사용하는 루프의 INT_MAX -1경우 종종 좋은 선택) :

for (int i = 0; i < 100; i++)
{
    for (int j = 0; j < 100; j++)
    {
        if (exit_condition)
        {
            // cause the outer loop to break:
            // use i = INT_MAX - 1; otherwise i++ == INT_MIN < 100 and loop will continue 
            i = int.MaxValue - 1;
            Console.WriteLine("Hi");
            // break the inner loop
            break;
        }
    }
    // if you have code in outer loop it will execute after break from inner loop    
}

코드에서 언급했듯이 break외부 루프의 다음 반복으로 마술로 건너 뛸 수는 없으므로 내부 루프 외부에 코드가 있으면이 방법에 더 많은 검사가 필요합니다. 이 경우 다른 솔루션을 고려하십시오.

이 방법은 작동 forwhile루프하지만 작동하지 않습니다 foreach. 의 경우 foreach(당신이 할 수 있더라도 당신이 그것을 변경할 수 있도록 숨겨진 열거에 코드를 액세스 할 수 없습니다 IEnumerator일부 "MoveToEnd"방법이 없습니다).

인라인 주석 작성자에 대한 감사의 글 : Meta의
i = INT_MAX - 1 제안 / ygoe의 의견 . 적절한 으로 jmbpiano 에 의해 내부 루프 후 코드에 대한 발언 blizpasta
forforeach
IntMax


@DrG : c #에서는 "break 문이 가장 가까운 둘러싸는 루프 나 switch 문을 종료합니다."로 작동하지 않습니다. (msdn)
blizpasta

1
@blizpasta 그래서? 그가 외부 루프에서 조건을 거짓으로 만들면 (그가했던 것처럼) 둘 다 종료합니다.
Patrick

51
i = INT_MAX-1을 사용해야합니다. 그렇지 않으면 i ++ == INT_MIN <100이고 루프는 계속됩니다
Meta

6
foreach에 대한 아이디어가 있습니까?
ktutnik

4
@ktutnik 숨겨진 열거 자에 대한 코드 액세스 권한이 없기 때문에 foreach와 함께 작동하지 않습니다. 또한 IEnumerator에는 "MoveToEnd"메소드가 없습니다.
ygoe

39

이 솔루션은 C #에는 적용되지 않습니다

다른 언어를 통해이 질문을 발견 한 사람들에게 Javascript, Java 및 D는 레이블이있는 구분을 허용하고 계속됩니다 .

outer: while(fn1())
{
   while(fn2())
   {
     if(fn3()) continue outer;
     if(fn4()) break outer;
   }
}

40
슬프게도 이것은 C #으로 수행 할 수 없으며 여러 번 더 깨끗한 코드를 생성합니다.
Rickard

17
나는 이것이 C #이 아니라는 것을 깨달을 때까지 실제로 흥분했다. :(
Arvo Bowen

1
이 개념은 누군가 누군가 문제를 겪을 경우를 대비 하여 PowerShell 에도 적용 됩니다. (그들은 단지 레이블 이름 앞에 콜론을 넣었습니다.) 이제 PowerShell과 관련이 없다는 것을 알고 있습니다 ...이 구문은 C #에서 사용할 수있는 goto 레이블과 호환되지 않습니다. PHP는 다른 것을 사용합니다 : break 3; break 문 뒤에 레벨 수를 입력하십시오.
ygoe

1
이것은 자바하지 # C입니다
루카 지글러

제발 😉 여기 칭찬을 배치, C #에서이 기능을보고 싶은 사람들을 위해 github.com/dotnet/csharplang/issues/869#issuecomment-326606003
m93a

28

외부 루프에 적절한 보호대를 사용하십시오. 차단하기 전에 내부 루프에 보호대를 설치하십시오.

bool exitedInner = false;

for (int i = 0; i < N && !exitedInner; ++i) {

    .... some outer loop stuff

    for (int j = 0; j < M; ++j) {

        if (sometest) {
            exitedInner = true;
            break;
        }
    }
    if (!exitedInner) {
       ... more outer loop stuff
    }
}

또는 내부 루프를 메소드로 추상화하고 false를 반환하면 외부 루프를 종료하십시오.

for (int i = 0; i < N; ++i) {

    .... some outer loop stuff

    if (!doInner(i, N, M)) {
       break;
    }

    ... more outer loop stuff
}

4
OP가 "부울을 사용하고 싶지 않다"고 말했다.
LeopardSkinPillBoxHat

매우 파스칼-쉬운 ... 나는 아마도 전염병처럼 피할 수 있지만 아마도 goto를 사용하고 싶습니다.
Jonathan Leffler

21

이것에 대해 인용하지는 않지만 MSDN에서 제안한대로 goto 를 사용할 수 있습니다 . 두 루프의 각 반복에서 확인되는 플래그를 포함하여 다른 솔루션이 있습니다. 마지막으로 예외를 문제에 대한 헤비급 솔루션으로 사용할 수 있습니다.

이동:

for ( int i = 0; i < 10; ++i ) {
   for ( int j = 0; j < 10; ++j ) {
      // code
      if ( break_condition ) goto End;
      // more code
   }
}
End: ;

질환:

bool exit = false;
for ( int i = 0; i < 10 && !exit; ++i ) {
   for ( int j = 0; j < 10 && !exit; ++j ) {
      // code
      if ( break_condition ) {
         exit = true;
         break; // or continue
      }
      // more code
   }
}

예외:

try {
    for ( int i = 0; i < 10 && !exit; ++i ) {
       for ( int j = 0; j < 10 && !exit; ++j ) {
          // code
          if ( break_condition ) {
             throw new Exception()
          }
          // more code
       }
    }
catch ( Exception e ) {}

2
이것들은 단지 메소드를 고려하고 초기 리턴을 사용하는 것이 매우 깔끔한 해키 해결책입니다.
Dustin Getz

3
:) 맞습니다. 간단한 해결책이지만, 필요한 모든 로컬 데이터를 인수로 메소드에 전달해야합니다. 이것은 goto가 적절한 솔루션이 될 수있는 몇 안되는 장소 중 하나입니다.
David Rodríguez-dribeas

조건 루프는 외부 루프를 종료하기 전에 내부 루프를 종료 한 후 "추가 코드"가 한 번 실행되므로 작동하지 않습니다. GOTO 방법은 작동하지만 포스터가 원하지 않는 것을 정확하게 수행합니다. Exception 메서드는 작동하지만 GOTO보다 더 느리고 느립니다.
Windows 프로그래머

레이블 뒤에 세미콜론을 강조 표시합니다. 이렇게하면 해당 레이블이 블록 끝에있을 수도 있습니다. +1
Tamas Hegedus

@Windowsprogrammer OP는 "이 문제에 대한 빠르고 좋은 방법은 무엇입니까?" Goto가 선호되는 솔루션입니다. 별도의 방법을 사용하지 않고 깨끗하고 간결합니다.
Suncat2000

16

중첩 된 for 루프를 개인 메소드로 리팩터링 할 수 있습니까? 그렇게하면 루프를 빠져 나가기 위해 단순히 메소드에서 '반환'할 수 있습니다.


만드는 측면 혜택으로 원래의 방법 짧은 :-)
마틴 Capodici

C ++ 11 람다는 다음과 같은 경우에 이것을 쉽게 [&] { ... return; ... }();
BCS

14

사람들이 goto진술을 많이 싫어하는 것 같습니다 . 그래서 조금 정리해야 할 필요성을 느꼈습니다.

사람들이 갖는 '감정'은 goto결국 코드에 대한 이해와 가능한 성능 영향에 대한 오해로 귀결됩니다. 질문에 대답하기 전에 먼저 컴파일 방법에 대해 자세히 설명하겠습니다.

우리 모두 알고 있듯이 C #은 IL로 컴파일 된 다음 SSA 컴파일러를 사용하여 어셈블러로 컴파일됩니다. 이 모든 것이 어떻게 작동하는지에 대해 약간의 통찰력을 제공하고 질문 자체에 답하려고 노력할 것입니다.

C #에서 IL로

먼저 C # 코드가 필요합니다. 간단하게 시작하자 :

foreach (var item in array)
{
    // ... 
    break;
    // ...
}

이 과정을 단계별로 수행하여 후드 아래에서 발생하는 일에 대한 좋은 아이디어를 제공합니다.

첫 번째 번역 :에서 foreach동등한 for루프로 (참고 : IDisposable에 대한 세부 정보를 얻고 싶지 않기 때문에 여기서 배열을 사용하고 있습니다.이 경우 IEnumerable도 사용해야합니다).

for (int i=0; i<array.Length; ++i)
{
    var item = array[i];
    // ...
    break;
    // ...
}

둘째 번역 다음 forbreak쉬운 동등한로 번역 :

int i=0;
while (i < array.Length)
{
    var item = array[i];
    // ...
    break;
    // ...
    ++i;
}

그리고 세 번째 번역 (IL 코드와 동일 함) : 분기를 변경 break하고 while분기합니다.

    int i=0; // for initialization

startLoop:
    if (i >= array.Length) // for condition
    {
        goto exitLoop;
    }
    var item = array[i];
    // ...
    goto exitLoop; // break
    // ...
    ++i;           // for post-expression
    goto startLoop; 

컴파일러는 단일 단계로 이러한 작업을 수행하지만 프로세스에 대한 통찰력을 제공합니다. C # 프로그램에서 발전한 IL 코드 는 마지막 C # 코드 의 리터럴 변환 입니다. https://dotnetfiddle.net/QaiLRz 에서 직접 확인할 수 있습니다. 보기 '클릭)

자, 여기서 관찰 한 것은 프로세스 중에 코드가 더 복잡해진다는 것입니다. 이것을 관찰하는 가장 쉬운 방법은 동일한 것을 인식하기 위해 점점 더 많은 코드가 필요하다는 것입니다. 또한 그 주장 수도 foreach, for, whilebreak에 대해 실제로 짧은 손입니다 goto부분적으로 사실이다.

IL에서 어셈블러로

.NET JIT 컴파일러는 SSA 컴파일러입니다. 여기서는 SSA 양식의 모든 세부 사항과 최적화 컴파일러를 만드는 방법에 대해 다루지 않을 것입니다. 너무 많지만 발생할 일에 대한 기본적인 이해를 줄 수 있습니다. 더 깊이 이해하려면 컴파일러 최적화에 대해 읽어 보는 것이 가장 좋습니다 (약간의 소개를 위해이 책을 좋아합니다 : http://ssabook.gforge.inria.fr/latest/book.pdf ) 및 LLVM (llvm.org) .

모든 최적화 컴파일러는 코드가 쉽고 예측 가능한 패턴을 따른다 는 사실에 의존 합니다 . FOR 루프의 경우 그래프 이론을 사용하여 분기를 분석 한 다음 분기의 cycli와 같은 항목 (예 : 뒤로 분기)을 최적화합니다.

그러나 이제 루프를 구현할 정방향 분기가 있습니다. 짐작 하셨겠지만, 이것은 실제로 JIT가 다음과 같이 고칠 첫 단계 중 하나입니다.

    int i=0; // for initialization

    if (i >= array.Length) // for condition
    {
        goto endOfLoop;
    }

startLoop:
    var item = array[i];
    // ...
    goto endOfLoop; // break
    // ...
    ++i;           // for post-expression

    if (i >= array.Length) // for condition
    {
        goto startLoop;
    }

endOfLoop:
    // ...

보시다시피, 우리는 이제 작은 분기점 인 뒤로 분기합니다. 여기서 여전히 불쾌한 유일한 것은 우리의 break진술 때문에 우리가 끝낸 지점입니다 . 어떤 경우에는 이것을 같은 방식으로 옮길 수 있지만 다른 경우에는 그대로 유지해야합니다.

그렇다면 왜 컴파일러가 이것을합니까? 루프를 풀면 벡터화 할 수 있습니다. 상수가 추가되었다는 것을 증명할 수도 있습니다. 이는 전체 루프가 얇은 공기로 사라질 수 있음을 의미합니다. 요약하면, 분기를 예측 가능하게하여 패턴을 예측 가능하게함으로써 루프에서 특정 조건이 유지되고 있음을 증명할 수 있습니다. 이는 JIT 최적화 중에 마술을 수행 할 수 있음을 의미합니다.

그러나 브랜치는 이러한 예측 가능한 멋진 패턴을 깨뜨리는 경향이 있으며, 이는 최적화 프로그램이므로 다소 싫어합니다. 깨고, 계속하고, 가십시오-그들은 모두 예측 가능한 패턴을 깨뜨 리려고하므로 실제로 '좋은'것은 아닙니다.

또한이 시점에서 단순한 foreach것이 더 예측 가능 하다는 것을 깨달아야 goto합니다. (1) 가독성과 (2) 옵티 마이저 관점에서 볼 때 더 나은 솔루션입니다.

언급해야 할 또 다른 사항은 레지스터를 변수에 할당하도록 컴파일러를 최적화하는 데 매우 관련이 있다는 것입니다 ( 레지스터 할당 이라는 프로세스 ). 아시다시피, CPU에는 레지스터 수가 한정되어 있으며 하드웨어에서 가장 빠른 메모리입니다. 가장 안쪽 루프에있는 코드에 사용 된 변수는 레지스터가 할당 될 가능성이 높지만 루프 외부의 변수는 덜 중요합니다 (이 코드가 적을 수 있기 때문에).

도움, 너무 많은 복잡성 ... 어떻게해야합니까?

결론은 항상 사용하는 언어 구조를 사용해야한다는 것입니다.이 구문은 일반적으로 컴파일러에 대해 예측 가능한 패턴을 빌드합니다. (: 특히 가능하면 이상한 가지 않도록하십시오 break, continue, goto또는 return아무것도의 중간에).

여기서 좋은 소식은 이러한 예측 가능한 패턴이 읽기 쉽고 (사람에게는), 쉽게 알아볼 수 있다는 것입니다 (컴파일러).

이러한 패턴 중 하나를 SESE라고하며 이는 단일 항목 단일 종료를 나타냅니다.

그리고 지금 우리는 실제 질문에 도달합니다.

다음과 같은 것이 있다고 상상해보십시오.

// a is a variable.

for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j)
  {
     // ...

     if (i*j > a) 
     {
        // break everything
     }
  }
}

이것을 예측 가능한 패턴으로 만드는 가장 쉬운 방법은 단순히 if완전히 제거하는 것입니다 .

int i, j;
for (i=0; i<100 && i*j <= a; ++i) 
{
  for (j=0; j<100 && i*j <= a; ++j)
  {
     // ...
  }
}

다른 경우에는 방법을 두 가지 방법으로 나눌 수도 있습니다.

// Outer loop in method 1:

for (i=0; i<100 && processInner(i); ++i) 
{
}

private bool processInner(int i)
{
  int j;
  for (j=0; j<100 && i*j <= a; ++j)
  {
     // ...
  }
  return i*j<=a;
}

임시 변수? 좋고 나쁘거나 못생긴가요?

루프 내에서 부울을 반환하기로 결정할 수도 있습니다 (하지만 SESE 양식을 개인적으로 선호합니다. 컴파일러가 그것을 보는 방식이므로 더 읽기 쉽다고 생각합니다).

어떤 사람들은 임시 변수를 사용하는 것이 더 깨끗하다고 ​​생각하고 다음과 같은 해결책을 제안합니다.

bool more = true;
for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j) 
  {
     // ...
     if (i*j > a) { more = false; break; } // yuck.
     // ...
  }
  if (!more) { break; } // yuck.
  // ...
}
// ...

나는 개인적으로이 접근법에 반대합니다. 코드가 어떻게 컴파일되는지 다시 살펴보십시오. 이제이 멋진 예측 가능한 패턴으로 이것이 무엇을하는지 생각해보십시오. 사진 가져와? 이해가 되세요?

맞아요, 철자를 쓰겠습니다. 일어날 일은 :

  • 컴파일러는 모든 것을 분기로 작성합니다.
  • 최적화 단계로서, 컴파일러는 more제어 흐름에서만 사용되는 이상한 변수 를 제거하기 위해 데이터 흐름 분석을 수행합니다 .
  • 성공하면 변수 more가 프로그램에서 제거되고 분기 만 남습니다. 이 분기는 최적화되므로 내부 루프에서 단일 분기 만 가져옵니다.
  • 실패하면 변수 more는 가장 안쪽 루프에서 확실히 사용되므로 컴파일러가 최적화하지 않으면 레지스터에 할당 될 가능성이 높습니다 (귀중한 레지스터 메모리를 소비합니다).

요약하자면, 컴파일러의 옵티마이 저는 more제어 흐름에만 사용되는 것을 알아내는 데 많은 어려움을 겪을 것이며 최상의 경우 시나리오 는 외부의 단일 분기로 변환합니다. 고리.

다시 말해, 가장 좋은 시나리오는 다음과 같은 결과를 낳을 것입니다.

for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j)
  {
     // ...
     if (i*j > a) { goto exitLoop; } // perhaps add a comment
     // ...
  }
  // ...
}
exitLoop:

// ...

이것에 대한 나의 개인적인 의견은 매우 간단합니다. 이것이 우리가 의도 한 것이라면 컴파일러와 가독성 모두를 위해 세상을 더 쉽게 만들고 즉시 작성하십시오.

tl; dr :

결론 :

  • 가능하면 for 루프에서 간단한 조건을 사용하십시오. 가능한 한 많이 사용하는 고급 언어 구성을 고수하십시오.
  • 모든 것이 실패하고 goto또는로 남아 있다면 bool more전자를 선호하십시오.

OTOH : 외부 루프를 끊거나 goto-equiv (그리고 더 깨끗하게 작성 될 수있는 많은 코드)를 원하는 경우는 거의 없지만, 다른 도메인에서는 더 흔할 수 있습니다. 그러나 그것은 주로 때문이었습니다 yield return. 즉, 몇 가지 유용한 사례가 있음을 쉽게 알 수 있지만 대부분의 "응용 프로그램 수준"코드의 경우 C / C ++에서 좌절이 발생할 가능성이 매우 낮습니다. 루비의 "throw"(비 예외 풀기)는 때때로이 영역에도 적합합니다.
user2864740

1
@ Suncat2000 변수를 사용 goto하지 않기 위해 변수 및 조건 분기와 같은 더 많은 코드를 도입 하면 코드를 더 읽기 쉽게 만들지 않습니다. 복잡성의 정의. 가독성을 높이기 위해 자기 훈련 소리를 보조하기 위해 그것을 피하는 것.
atlaste

@atlaste : 죄송합니다. 내 의견을 더 명확하게 설명하겠습니다. 내가 말해야 할 것은 : 좋은 설명이었습니다. 그러나 goto를 피하는 사람들은 성능에 관한 것이 아닙니다. 가독성을 떨어 뜨리는 남용을 피하는 것입니다. 우리는 피하는 사람들이 goto그것을 학대하지 않을 규율이 없다고 가정 할 수 있습니다.
Suncat2000

@atlaste, "ackomplish"는 어떤 언어입니까?
Pacerier

11

함수 / 메서드에 포함시키고 조기 리턴을 사용하거나 루프를 while 절로 재정렬하십시오. goto / exceptions / 무엇이 여기에 적합하지 않은지.

def do_until_equal():
  foreach a:
    foreach b:
      if a==b: return

10

빠르고, 부울을 사용하지 않고, goto를 사용하지 않고, C #을 조합하여 요청했습니다. 당신은 당신이 원하는 것을 할 수있는 모든 가능한 방법을 배제했습니다.

가장 빠르고 가장 추한 방법은 goto를 사용하는 것입니다.


전적으로 동의합니다. 단일 goto를 제거하는 새로운 방법을 도입하는 것은 어리석은 일입니다. 컴파일러가 어떤 이유로 든 해당 메소드 호출을 인라인 할 수없는 경우 추가 메소드 호출의 불필요한 오버 헤드가 발생합니다. 루프를 깨기 위해 예외를 던지고 잡는 것은 더 장황하고 엄청나게 비쌉니다.
Zar Shardan

@Windowsprogramm : OP에서 "goto 사용 안함"을 요청하지 않았습니다. 그는 "다른 방법으로 가고 싶지 않았다". 문제는 가능한 모든 방법 을 배제하는 것이 아니 었지만 여기서는 goto가 가장 좋다는 것을 암시하는 것이 옳습니다.
Suncat2000

5

때로는 코드를 자체 함수로 추상화하고 조기 반환을 사용하는 것보다 좋습니다.

public void GetIndexOf(Transform transform, out int outX, out int outY)
{
    outX = -1;
    outY = -1;

    for (int x = 0; x < Columns.Length; x++)
    {
        var column = Columns[x];

        for (int y = 0; y < column.Transforms.Length; y++)
        {
            if(column.Transforms[y] == transform)
            {
                outX = x;
                outY = y;

                return;
            }
        }
    }
}

1
그러나 이것은 OP에 의해 요청 된 전통적인 해결책을 허용합니다
Surya Pratap

2

"break"를 사용하지만 "continue"를 사용하지 않는 많은 예제를 보았습니다.

여전히 내부 루프에서 일종의 플래그가 필요합니다.

while( some_condition )
{
    // outer loop stuff
    ...

    bool get_out = false;
    for(...)
    {
        // inner loop stuff
        ...

        get_out = true;
        break;
    }

    if( get_out )
    {
        some_condition=false;
        continue;
    }

    // more out loop stuff
    ...

}

1

제가 break수십 년 전 C에서 처음 본 이후 로이 문제는 저를 괴롭 혔습니다. 일부 언어 향상 기능이 중단되어 확장 기능이 작동하기를 희망했습니다.

break; // our trusty friend, breaks out of current looping construct.
break 2; // breaks out of the current and it's parent looping construct.
break 3; // breaks out of 3 looping constructs.
break all; // totally decimates any looping constructs in force.

3
그런 다음 유지 보수 프로그래머는 다른 레벨의 중첩을 삽입하고 일부 break 문을 수정하고 다른 break 문 중 일부를 중단합니다. 그에 대한 해결책은 대신 레이블로 나누는 것입니다. 그것은 실제로 제안되었지만 실용 주의자들은 대신 goto 레이블을 사용합니다.
Windows 프로그래머

잠깐, 누가 더 이상 유지 보수 프로그래밍을합니까? :)
Jesse C. 슬라이서

2
JavaScript는 심지어 블록 / 브레이크 문에 레이블을 붙였습니다. devguru.com/Technologies/ecmascript/quickref/break.html
David Grant

@Chris Bartow : 멋지다! 내 크리스마스 :) @David Grant : 그래서 JS break == C의 goto 인 것 같습니까?
Jesse C. Slicer

D는 중단 / 연속이라고 표시했습니다
BCS

0

나는 학생 시절부터 goto없이 코드로 무엇이든 할 수 있다는 것이 수학적으로 입증되었다는 것을 기억합니다 (즉 goto가 유일한 대답 인 상황은 없습니다). 그래서 나는 결코 goto를 사용하지 않습니다 (개인 취향에 따라 옳고 그름을 제안하지는 않습니다)

어쨌든 중첩 루프에서 벗어나려면 다음과 같이하십시오.

var isDone = false;
for (var x in collectionX) {
    for (var y in collectionY) {
        for (var z in collectionZ) {
            if (conditionMet) {
                // some code
                isDone = true;
            }
            if (isDone)
                break;
        }
        if (isDone) 
            break;
    }
    if (isDone)
        break;
}

... 나는 그것이 나를 좋아하는 사람들에게 반 고토 "fanboys"를 돕기를 바랍니다 :)


유감스럽게도 강사는 조건에 대한 책임이 있습니다. 그가 당신에게 어셈블리를 배우도록 강요했다면, 'goto'는 단지 점프라는 것을 알고 있습니다 (물론 이것은 AC # 질문이라는 사실을 무시하고 있습니다).
cmroanirgo

0

그렇게 했어요 여전히 해결 방법입니다.

foreach (var substring in substrings) {
  //To be used to break from 1st loop.
  int breaker=1;
  foreach (char c in substring) {
    if (char.IsLetter(c)) {
      Console.WriteLine(line.IndexOf(c));
      \\setting condition to break from 1st loop.
      breaker=9;
      break;
    }
  }
  if (breaker==9) {
    break;
  }
}


0

이중 루프를 끝내는 가장 쉬운 방법은 첫 번째 루프를 직접 종료하는 것입니다.

string TestStr = "The frog jumped over the hill";
char[] KillChar = {'w', 'l'};

for(int i = 0; i < TestStr.Length; i++)
{
    for(int E = 0; E < KillChar.Length; E++)
    {
        if(KillChar[E] == TestStr[i])
        {
            i = TestStr.Length; //Ends First Loop
            break; //Ends Second Loop
        }
    }
}

break를 사용하면 내부 루프가 종료되므로 작동하지 않습니다. 외부 루프는 계속 반복됩니다.
Alex Leo

0

루프에서 사용자 정의 조건을 사용하여 루프를 끊을 수 있으므로 깨끗한 코드를 가질 수 있습니다.

    static void Main(string[] args)
    {
        bool isBreak = false;
        for (int i = 0; ConditionLoop(isBreak, i, 500); i++)
        {
            Console.WriteLine($"External loop iteration {i}");
            for (int j = 0; ConditionLoop(isBreak, j, 500); j++)
            {
                Console.WriteLine($"Inner loop iteration {j}");

                // This code is only to produce the break.
                if (j > 3)
                {
                    isBreak = true;
                }                  
            }

            Console.WriteLine("The code after the inner loop will be executed when breaks");
        }

        Console.ReadKey();
    }

    private static bool ConditionLoop(bool isBreak, int i, int maxIterations) => i < maxIterations && !isBreak;   

이 코드를 사용하여 다음과 같은 출력을 얻습니다.

  • 외부 루프 반복 0
  • 내부 루프 반복 0
  • 내부 루프 반복 1
  • 내부 루프 반복 2
  • 내부 루프 반복 3
  • 내부 루프 반복 4
  • 내부 루프 뒤의 코드는 중단되면 실행됩니다.

0

다른 옵션은 자체 호출 익명 함수 입니다. goto, 레이블 없음, 새 변수 없음, 새 function-name 사용 안함 및 맨 위 의 anonymous-method 예제 보다 한 줄 더 짧은 줄 .

// self-invoked-anonymous-function
new Action(() =>
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits self invoked lambda expression
        }
    }
})();
Console.WriteLine("Hi");

-3

외부 루프가 발생하는 사용자 정의 예외를 처리하십시오.

그것은 for, foreach또는 while모든 종류의 루프와 try catch exception블록 을 사용하는 모든 언어에서 작동합니다.

try 
{
   foreach (object o in list)
   {
      foreach (object another in otherList)
      {
         // ... some stuff here
         if (condition)
         {
            throw new CustomExcpetion();
         }
      }
   }
}
catch (CustomException)
{
   // log 
}

-4
         bool breakInnerLoop=false
        for(int i=0;i<=10;i++)
        {
          for(int J=0;i<=10;i++)
          {
              if(i<=j)
                {
                    breakInnerLoop=true;
                    break;
                }
          }
            if(breakInnerLoop)
            {
            continue
            }
        }

Dviljoen의 답변과 본질적인 차이점은 무엇입니까?
게르트 아놀드

외부 for 루프에서 "breakInnerLoop"조건을 확인하지 않으므로 다음 루프로 반복하고 j와 J를 작성하고 세미콜론을 놓치면 컴파일되지 않습니다.
Sebastian

-4

나는 당신이 그 사람이 당신에게 goto 진술을 언급하는 대답을 받아 들였다는 것을 알았습니다. 현대 프로그래밍과 전문가의 견해로는 goto가 살인자입니다. 이 시점에서 질문의 해결책은 매우 간단합니다.이 예제에서 부울 플래그를 사용할 수 있습니다. 예를 들어 설명하겠습니다.

            for (; j < 10; j++)
            {
                //solution
                bool breakme = false;
                for (int k = 1; k < 10; k++)
                {
                   //place the condition where you want to stop it
                    if ()
                    {
                        breakme = true;
                        break;
                    }
                }

                if(breakme)
                    break;
               }

간단하고 평범합니다. :)


휴식을 제안하기 전에 질문을 읽으십시오. 따라서 downvotes.
cmroanirgo

1
지금 읽으십시오.하지만이 답변을 게시 할 때 부울을 사용하지 않는 편집 버전이 추가되지 않았기 때문에이 답변을 게시 한 이유는 .... downvote 주셔서 감사합니다!
Steve

1
개인적인 것이 아닙니다. 불행히도, 투표는 이제 막혔습니다;) 최고에 대한 최고의 답변을 얻는 데 필요한 부분입니다 (항상 그런 것은 아님)
cmroanirgo

-6

break키워드를 보았습니까 ? 우

이것은 의사 코드 일 뿐이지 만 의미하는 것을 볼 수 있습니다.

<?php
for(...) {
    while(...) {
        foreach(...) {
            break 3;
        }
    }
}

break와 같은 함수 에 대해 생각하면 break()매개 변수는 벗어날 루프 수입니다. 여기서 코드의 세 번째 루프에 있으므로 세 가지를 모두 벗어날 수 있습니다.

매뉴얼 : http://php.net/break


13
PHP 질문이 아닙니다.
Zerga

-10

당신이 "부울 일"을하고 싶지 않다면 유일한 해결책은 실제로 던질 것이라고 생각합니다. 당신은 분명히해서는 안되는 것 ..!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.