我想知道当验证结束条件(不同于正确的迭代次数)时是否可以在C ++中结束for循环。例如:
for (int i = 0; i < maxi; ++i)
for (int j = 0; j < maxj; ++j)
// But if i == 4 < maxi AND j == 3 < maxj,
// then jump out of the two nested loops.
我知道这可以在Perl中使用下一个LABEL或最后一个LABEL调用和标记块,是否可以在C ++中执行,或者我应该使用while循环?
谢谢。
答案 0 :(得分:51)
您可以使用return
关键字:将嵌套循环移动到子例程中,调用子例程以运行嵌套循环,并从子例程中“返回”以退出[all]循环。
答案 1 :(得分:47)
尽管“goto
被认为是有害的”论点,但这似乎是goto
的理想之地。这基本上就是你在Perl中所做的。说真的......考虑其他选择:
额外状态变量
for (int i=0; i<maxi; ++i) {
bool leaveLoop = false;
for (int j=0; j<maxj; ++j) {
if (i == 4 && j == 3) {
leaveLoop = true;
break; // leave the inner loop
}
}
if (leaveLoop) {
break; // leave the outside loop
}
}
以异常方式离开
try {
for (int i=0; i<maxi; ++i) {
for (int j=0; j<maxj; ++j) {
if (i == 4 && j == 3) {
throw leave_loop();
}
}
}
} catch (leave_loop const&) {
}
复杂逻辑
int j = 0;
for (int i=0; i<maxi && !(i==4 && j==3); ++i) {
for (j=0; j<maxj && !(i==4 && j==3); ++j) {
// inner loop
}
}
<强> goto
强>
for (int i=0; i<maxi; ++i) {
for (int j=0; j<maxj; ++j) {
if (i==4 && j==3) {
goto leave_loop;
}
}
}
leave_loop:
最后一个不太清楚吗?我不相信它。它更脆弱了吗?恕我直言,与goto
版本相比,其他版本容易出错且易碎。很抱歉在这里站在肥皂盒上,但这件事困扰了我一段时间;)
你唯一需要承认的是goto
和异常非常相似。它们都为泄漏资源提供了机会,并且不小心对待它们。
答案 2 :(得分:13)
让我强调(但礼貌地;-)尽可能地说: c-like语言中的for
构造与计算无关。
确定是否继续的测试表达式可以是与循环目的相关的任何内容;更新表达式不必是“将一个添加到计数器”。
for (int i = 0, j = 0; i < maxi && j < maxj && i != 4 && j != 3;) {
if (j < maxj) {
++j;
} else {
j = 0;
++i;
}
}
将是一种(相当随意的)重写方式。
关键是如果建立一些条件是一个交互点,通常可以用一种方式来表示一个循环(使用while
或for
)来表示继续/终止条件更明确地说。
(如果你可以发布实际发生的事情的描述,它可能会写出一些看起来不像上面那样任意的东西。)
答案 3 :(得分:8)
你不能用一个中断指令跳出两个循环,但你可以使用goto从内部循环跳到外面。
如果goto是本地化的,意味着逻辑少于其他我认为它是完全可以接受的代码。有额外的标志变量,或者从内循环中提取迭代器变量,这样你就可以在外循环中对它进行比较,这样就不会更容易理解代码恕我直言。
答案 4 :(得分:8)
从上面的所有建议中我都会避免使用try / catch机制,因为异常应该保留用于特殊情况,而不是正常的控制流。
如果您可以适当地创建第二个条件,则可以使用两个中断。为此目的使用布尔值也很好,你甚至可以将它连接到每个for循环的条件。例如:
bool exit_loops = false;
for (int a = 0; a < A && !exit_loops; ++a)
{
for (int b = 0; b < B && !exit_loops; ++b)
{
if (some_condition) exit_loops = true;
}
}
虽然如果你使用两个以上的循环,将它们包装在一个函数中可能更合适,只需使用return退出函数(以及所有的循环)。然后,您可以通过调用函数执行内部循环代码等方式重构代码,从而消除除一个循环之外的所有循环,等等。
最后不要害怕在这种情况下使用goto,通常goto是糟糕的非结构化编程,但在某些情况下(像这样)它们非常有用。
答案 5 :(得分:6)
你不能在C / C ++中这样跳出来:
for (...)
{
for (...)
{
// from here...
}
}
// ...to here
不使用goto。你需要一个像:
这样的结构for (...)
{
bool
exit = false;
for (...)
{
if (do_exit)
{
exit = true; // or set outer loop counter to end value
break;
}
}
if (exit)
{
break;
}
}
或者,使用throw和catch - 但这并不好,因为throw应该用于异常而不是流量控制。
一种干净的方法是使内循环成为一个函数:
bool F ()
{
if inner loop terminates, return false else return true
}
void G ()
{
for (...)
{
if (!F ())
{
break;
}
}
}
答案 6 :(得分:6)
bool done = false;
for (int i = 0; i < maxi && !done; ++i)
for (int j = 0; j < maxj && !done; ++j)
if (i == 4 && i < maxi && j == 3 && j < maxj )
done = true;
else {
}
或者你可以转到。或不: - )
答案 7 :(得分:4)
for (int i = 0; i < maxi; ++i)
{
int j = 0;
for (j = 0; j < maxj; ++j)
{
if (i == 4 && j == 3) // i < maxi and j < maxj otherwise we would not be here
break; // exit inner loop
}
if (i == 4 && j == 3) // i < maxi and j < maxj otherwise we would not be here
break; // exit outer loop
}
答案 8 :(得分:4)
您可以使用goto statement,但这通常被视为不良做法。
你的另一个选择是做这样的事情
int i;
int j = 0;
for (i = 0; i < maxi && !(i==4 && j==3); ++i)
for (j = 0; j < maxj && !(i==4 && j==3); ++j)
答案 9 :(得分:2)
阅读代码不应该像阅读一本侦探书(总是需要弄清楚)......
一个例子:
爪哇:
iterate_rows:
for (int i = 0; i < maxi; ++i)
{
for (int j = 0; j < maxj; ++j)
{
if (i == 4 < maxi && j == 3 < maxj)
break iterate_rows;
else
continue iterate_rows;
}
}
你不需要弄清楚iterate_rows的中断,你只需要阅读它。
C ++:
//iterate_rows:
for (int i = 0; i < maxi; ++i)
{
for (int j = 0; j < maxj; ++j)
{
if (i == 4 < maxi && j == 3 < maxj)
goto break_iterate_rows;
else
goto continue_iterate_rows;
}
continue_iterate_rows:;
}
break_iterate_rows:;
转到break_iterate_rows 只是 break iterate_rows 的可见版本
如果您仅限于使用此类代码的goto和标签,则无法确定其意图。限制在这种代码上使用goto和标签将使您只需阅读代码,而不是分析或计算代码。你不会被指责为邪恶的程序员。
如果你真的限制你在这种代码中使用了这些代码,那么你就可以养成一种习惯,即不需要弄清楚你的代码中有什么用。额外的好处是您不必引入布尔值并跟踪它们(这会导致您检测代码,使其有点难以理解,这会破坏避免使用的目的)
P.S。
将这些标签与注释配对(在循环之前),当您通过goto语句读取这些行时,您已经知道那些gotos的意图
答案 10 :(得分:1)
您可以使用以下内容的标签:
Outer:
for(...)
{
Inner:
for(...)
{
if(condition)
{
goto End;
}
}
}
End:
在Java中你可以通过标签来打破我的想法吗?
编辑 - 将goto更改为End而不是Outer,但我不认为否定代表是合理的。这个答案给出了最简单的方法。
答案 11 :(得分:1)
我有一些建议:
throw ....将两个循环放在“try {}”中,然后“抓住”条件下的“throw”。
将两个循环放在一个方法中并返回条件。
Goto并不是邪恶的,它的使用人们也把它....你可以使用“goto”它可以是最清晰的代码,尤其是在处理错误时。我20年没用过了。
贝
答案 12 :(得分:1)
我一直试图远离goto
陈述(出于某种原因,总是被学校和我的工作所忽视)。我会使用Daemin建议的东西。
答案 13 :(得分:1)
完全重新考虑for
结构的另一个原因是它的作用域阻止了在循环终止后访问受控变量。在循环中变异的变量的值可能由于各种原因(例如,在搜索中区分成功与失败)而有用,否则在范围退出之后需要额外的变量来保存该信息。这是一个小例子,用于搜索名为a
的方形数组中的target
值(假设SIZE
非零,否则无需搜索!):
int i = 0;
int j = 0;
while (i < SIZE && a[i][j] != target) { // still in array but not at target
if (SIZE <= ++j) { // fallen off the end of a row
j = 0;
++i;
}
}
后续代码可以使用i < SIZE
来确定是否找到了所需的值。
上述的另一个优点是灵活性。假设我们现在被告知a
行中的值是递增的,因此如果遇到大于target
的值,则行的其余部分无关紧要。很容易确切地知道要做出什么样的改变,以及在哪里做出改变。因为新信息允许我们放弃当前行,所以只有内部决策受到影响,成为:
if (target < a[i][j] || SIZE <= ++j) { // can't be in row or fallen off end
...
我看到更多新的语言(特别是功能导向的语言)放弃旧的“计数”循环结构;这可能是件好事,因为它鼓励我们考虑循环的含义,而不是简单地计算。
答案 14 :(得分:1)
The best way I've seen涉及宏和gotos,但它实际上相当不错(链接到帖子开始时谈论Perl,但最后一段左右引入了宏)。
它允许您编写如下代码:
named (LOOPS) for (i=1; i<10; i++) {
for (j=1; j<10; j++) {
for (j=1; j<10; j++) {
/* Process data[i][j][k] here */
if (data[i][j][k] < threshold) break(LOOPS);
}
}
}