什么是一次摆脱许多for循环的巧妙方法?

问题描述 投票:16回答:14

假设我需要在最内层循环内的某个事件发生时立即中断三个或四个嵌套for循环。这样做的方法是什么?

我做的是使用这样的标志:

int i, j, k;
int flag1 = 0;
int flag2 = 0;

for (i = 0; i < 100; i++) {
    for (j = 0; j < 100; j++) {
        for (k = 0; k < 100; k++) {
            if (k == 50) {
                flag1 = 1;
                flag2 = 1;
                break;
            }
        }
        if (flag1 == 1)break;
    }
    if (flag2 == 1)break;
}

我认为这不是特别整洁。

你会如何完成同样的事情? (没有使用跳跃)

c for-loop break
14个回答
85
投票

使用转到。它干净而简单。


2
投票

一点点愚蠢的自我记录:

int i, j, k;
int done = 0;

for (i = 0; i < 100 && ! done; i++) {
    for (j = 0; j < 100 && ! done; j++) {
        for (k = 0; k < 100 && ! done; k++) {
            if (k == 50) we_are(done);
        }
    }
}

//...

void we_are(int *done) {
    *done = 1;
}

但实际上,你不应该有三个嵌套的for循环。您应该考虑重构为不同的函数并改进程序的逻辑,而不是这样做。

虽然我同意有时goto确实是最好的解决方案,但我认为goto解决方案的任何问题都是代码不佳的结果。


0
投票

除以0是我所知道的最可靠的方法,可以让你摆脱任意数量的循环。这是有效的,因为DIV组装指令不喜欢这种愚蠢。

所以,你可以试试这个:

int i, j, k;
int flag1 = 0;
int flag2 = 0;

for (i = 0; i < 100; i++) {
    for (j = 0; j < 100; j++) {
        for (k = 0; k < 100; k++) {
            if (k == 50) {
                flag1 = 1;
                flag2 = 1;
                int z = 1 / 0;  // we're outta here!!!
            }
        }
        if (flag1 == 1)break;
    }
    if (flag2 == 1)break;
}

从这些事件中发生的trap回来留下作为读者的练习(这是微不足道的)。


0
投票

我会这样做:

  int i, j, k;

  for (i = 0; i < 100; i++) {
      for (j = 0; j < 100; j++) {
          for (k = 0; k < 100; k++) {
              if (k == 50) {
                  return;
              }
          }
      }
  }

0
投票

如果您使用GCC和this librarybreak可以接受您要退出的嵌套循环的数量:

int i, j, k;

for (i = 0; i < 100; i++) {
    for (j = 0; j < 100; j++) {
        for (k = 0; k < 100; k++) {
            if (k == 50) {
                break(3);
            }
        }
    }
}

-1
投票

一种方法是使用状态机。但我仍然会使用goto。它简单得多。 :)

state = 0;
while( state >= 0){
    switch(state){
        case 0: i = 0; state = 1; // for i = 0
        case 1:
            i++; 
            if (i < 100)   // if for i < 100 not finished
                state = 2; // do the inner j loop
            else
                state = -1; // finish loop
        case 2: j = 0; state = 3; // for j = 0
        case 3: 
            j++;
            if (j < 100)  // if j < 100 not finished
                state = 4 // do the inner k loop
            else
                state = 1; // go backt to loop i
            break;
        case 4: k = 0; state = 5;
        case 5:
            k++;
            if (k == 50){
                state = -1;
                break;
            }
            if (k < 100) // if k loop not finished
                state = 5; // do this loop
            else
                state = 3; // go back to upper loop
            break;
        default : state = -1;
    }
}

49
投票

将所有循环放在函数中,然后返回而不是中断。


17
投票

如果您正在使用Java,则可以将标签与每个块关联,然后在continue语句后引用标签。例如:

outerfor:
for (int i=0; i<5; i++) {
    innerfor:
    for (int j=0; j<5; j++) {
        if (i == 1 && j == 2) {
             continue outerfor;
        }
    }
}

14
投票

你会如何完成同样的事情? (没有使用跳跃)

为什么?没有什么是普遍的邪恶,每一个附加工具都有它的用途(除了gets())。在这里使用goto使您的代码看起来更清晰,并且是我们唯一的选择之一(假设C)。看:

int i, j, k;

for (i = 0; i < 100; i++) {
    for (j = 0; j < 100; j++) {
        for (k = 0; k < 100; k++) {
            if (k == 50) {
                goto END;
            }
        }
    }
}
END:

比所有这些标志变量更清晰,它甚至可以更清楚地显示您的代码正在做什么。


5
投票

只是一点点好。

int i, j, k;
int flag1 = 0;
int flag2 = 0;

for (i = 0; i < 100 && !flag2; i++) {
    for (j = 0; j < 100 && !flag1; j++) {
        for (k = 0; k < 100; k++) {
            if (k == 50) {
                flag1 = 1;
                flag2 = 1;
                break;
            }
        }
    }
}

但是如果你确实需要这些循环,那么为了便于阅读,在每个循环中明确声明必须保持哪些条件才能继续循环。


4
投票

goto。这是goto是适当工具的极少数地方之一,并且通常是为什么goto不完全邪恶的论据。

但有时,我这样做:

void foo() {
    bar_t *b = make_bar();
    foo_helper(bar);
    free_bar(b);
}

void foo_helper(bar_t *b) {
    int i,j;
    for (i=0; i < imax; i++) {
        for (j=0; j < jmax; j++) {
            if (uhoh(i, j) {
                return;
            }
        }
    }
}

这个想法是我得到一个保证免费的酒吧,加上我通过返回得到一个干净的两级突破。


4
投票

如果您绝对不想使用goto,请将所有循环条件设置为false:

int i, j, k;

for (i = 0; i < 100; i++) {
    for (j = 0; j < 100; j++) {
        for (k = 0; k < 100; k++) {
            if (k == 50) {
                i = j = k = INT_MAX;
                break;
            }
        }
    }
}

注意:智能优化编译器会将跳转中if的内容转换为最外层循环的末尾


3
投票

有时你可以使用这样的技巧:

for (i = 0; i < 100 && !flag2; i++) {
for (j = 0; j < 100 && !flag1; j++) {
    for (k = 0; k < 100; k++) {
        if (k == 50) {
            k = 100;
            i = 100;
            j = 100;
        }
    }
}

}

或在循环中声明添加标志:

bool end = false;
for(int i =0; i < 1000 && !end; i++) {
   //do thing
   end = true;
}

我想,它只需要一条线但干净。

贾斯汀


2
投票

如果过早完成任何循环总是意味着你必须打破封闭循环,那么你不需要任何额外的标志。整个事情可能看起来如下

int i, j, k;
for (i = 0; i < 100; i++) {
    for (j = 0; j < 100; j++) {
        for (k = 0; k < 100; k++) {
            if (k == 50)
                break;
        }
        if (k < 100) break;
    }
    if (j < 100) break;
}

根据我的经验,这是大多数情况下所需要的。

© www.soinside.com 2019 - 2024. All rights reserved.