如何从交换机内部打破循环?

jrh*_*ath 108 c++ syntax loops break switch-statement

我正在写一些看起来像这样的代码:

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

有没有直接的方法呢?

我知道我可以使用一个标志,并通过在切换后放置一个条件中断来从循环中断.我只是想知道C++是否已经有了一些构造.

Meh*_*ari 154

你可以用goto.

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

   }
}
exit_loop: ;
Run Code Online (Sandbox Code Playgroud)

  • 我很有趣,因为人们不喜欢`goto`.OP明确提到*而不使用标志*.考虑到OP的限制,你能建议一个更好的方法吗?:) (42认同)
  • 赞成只是为了补偿无意识的结果仇恨者.我猜Mehrdad知道他在这里建议明智的解决方案会失去一些积分. (15认同)
  • 只是不要狂野使用该关键字. (12认同)
  • 在一天结束时,没有goto结构,所有语言都会失败.高级语言会混淆它,但是如果你在引擎盖下看起来很难,你会发现每个循环,或条件归结为条件和无条件分支.我们通过使用for,while,until,切换,如果是关键字来欺骗自己,但最终它们都以某种方式利用GOTO(或JMP).你可以欺骗自己,发明各种聪明的方法来隐藏它,或者你可以只是务实诚实,并在适当的时候谨慎地使用goto. (10认同)
  • +1,即使在考虑OP限制时,也没有更好的方法.标志是丑陋的,在整个循环中分解逻辑,因此更难以掌握. (6认同)
  • +1.我知道这更像是一个理论问题而不是一个实际问题; 它明确要求跳转指令.鉴于break,continue和return是不合适的,唯一的答案是一般的跳转:goto.这说,虽然(旗帜)将是一个优越的结构,但不是OP所要求的. (5认同)
  • 这是我考虑过的极少数领域之一.它向前发展,超出了范围,而不是在范围内.它实现了一个非常容易理解的概念.goto的问题在于它很容易以非结构化的方式使用(并且很少有人使用它,因此它相当模糊).在我看来,这是一种可接受和适当的用途. (5认同)
  • 你知道我看到一个'goto`答案真的有点惊讶.不过,我很高兴你做到了,所以我没有.+1 (2认同)

Dav*_*vis 53

前提

无论语言或所需功能如何,以下代码都应视为错误形式:

while( true ) {
}
Run Code Online (Sandbox Code Playgroud)

支持论证

while( true )循环是拙劣的形式,因为它:

  • 打破while循环的隐含契约.
    • while循环声明应明确说明唯一的退出条件.
  • 意味着它永远循环.
    • 必须读取循环内的代码才能理解终止子句.
    • 永远重复的循环会阻止用户从程序中终止程序.
  • 效率低下.
    • 有多个循环终止条件,包括检查"真".
  • 很容易出错.
    • 无法轻松确定将每次迭代始终执行的代码放在何处.
  • 导致不必要的复杂代码.
  • 自动源代码分析.
    • 要查找错误,程序复杂性分析,安全检查或自动派生任何其他源代码行为而无需执行代码,指定初始中断条件允许算法确定有用的不变量,从而改进自动源代码分析指标.
  • 无限循环.
    • 如果每个人总是使用while(true)非无限循环,那么当循环实际上没有终止条件时,我们就失去了简洁通信的能力.(可以说,这已经发生了,所以重点是没有意义.)

"去"的替代方案

以下代码是更好的形式:

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

bool isValidState() {
  return msg->state != DONE;
}
Run Code Online (Sandbox Code Playgroud)

好处

没有国旗.不goto.没有例外.容易改变.易于阅读.易于修复.另外代码:

  1. 将循环工作负载的知识与循环本身隔离开来.
  2. 允许维护代码的人轻松扩展功能.
  3. 允许在一个位置分配多个终止条件.
  4. 将终止子句与要执行的代码分开.
  5. 对核电厂来说更安全.;-)

第二点很重要.在不知道代码是如何工作的情况下,如果有人让我做主循环让其他线程(或进程)有一些CPU时间,我会想到两个解决方案:

选项1

随便插入暂停:

while( isValidState() ) {
  execute();
  sleep();
}
Run Code Online (Sandbox Code Playgroud)

选项#2

覆盖执行:

void execute() {
  super->execute();
  sleep();
}
Run Code Online (Sandbox Code Playgroud)

此代码比具有嵌入式的循环更简单(因此更易于阅读)switch.该isValidState方法应仅确定循环是否应继续.该方法的主力应被抽象成execute方法,它允许子类覆盖默认行为(一个困难的任务使用嵌入式switchgoto).

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!' 
Run Code Online (Sandbox Code Playgroud)

与:

  1. 初始化用户的选择.
  2. 循环,而用户选择的单词是"重启".
  3. 要求用户输入他们的选择.
  4. 结束.
choice = 'restart';

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

print 'Break!'
Run Code Online (Sandbox Code Playgroud)

在这里,while True导致误导和过于复杂的代码.

  • "while(true)"应该是有害的想法对我来说似乎很奇怪.在执行之前有循环检查,之后会检查那些循环,并且那些是在中间检查的循环.由于后者在C和C++中没有语法结构,因此你必须使用`while(true)`或`for(;;)`.如果你认为这是错误的,你还没有充分考虑到不同类型的循环. (34认同)
  • 我不同意的原因:while(true)和for(;;;)并不混淆(不像do {} while(true))因为它们预先说明了他们正在做的事情.实际上,查找"break"语句比解析任意循环条件并查找其设置位置更容易,并且更难以证明正确性.关于在何处放置代码的论点在存在continue语句时同样难以处理,而if语句则更好.效率论证是愚蠢的. (31认同)
  • 无论何时看到"while(true)",您都可以想到一个具有内部终止条件的循环,这种条件并不比任意结束条件更难.简单地说,"while(foo)"循环,其中foo是以任意方式设置的布尔变量,并不比具有中断的"while(true)"更好.在这两种情况下,您都必须查看代码.顺便说一句,提出一个愚蠢的理由(微观效率是一个愚蠢的论据)对你的情况没有帮助. (20认同)
  • @Dave:我给出了一个理由:这是获得一个检查中间条件的循环的唯一方法.经典例子:`while(true){string line; 的std ::函数getline(是,线); 如果(!是)打破; lines.push_back(line);}`当然我可以将它转换为预处理循环(使用`std :: getline`作为循环条件),但这本身就有缺点(`line`必须是变量在循环的范围之外).如果我这样做,我将不得不问:为什么我们还有三个循环呢?我们总是可以将所有内容转换为预处理循环.使用最合适的.如果`while(true)`适合,那么使用它. (5认同)
  • 出于对阅读此答案的人的礼貌,我会避免评论产生该问题的商品的质量,并坚持回答问题本身。问题是以下问题的变体,我相信这是许多语言的一个特征:您有一个嵌套在另一个循环内的循环。您想从内循环中跳出外循环。这是怎么做到的? (3认同)
  • @Dave Jarvis:你的 Python 代码中的 `continue` 是完全没有必要的。用一些虚拟值初始化 `choice` 是容易出错的部分。 (2认同)
  • 这个答案完全忽略了在中间某处具有终止条件的循环的非常有效的情况(如果这不是一个合适的用例,我们甚至不会有一个break关键字),以便妖魔化goto。将中间有中断的 while(true) 循环转换为 while(cond) 或 do{}while(cond) 形式需要人工标志或代码重复,这两者都很难实现阅读并争论。最后,如果您担心 `while(true)` 的性能影响,您可以随时将其更改为 `for(;;)` (2认同)

sak*_*kra 53

一种替代的解决方案是使用关键字continue结合break,即:

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

使用该continue语句完成您希望循环继续的每个案例标签,并使用该break语句完成应终止循环的案例标签.

当然,只有在switch语句之后没有其他代码要执行时,此解决方案才有效.

  • 虽然这确实非常优雅,但它的缺点是大多数开发人员首先必须查看它一分钟才能理解这是如何工作的.`(` (8认同)
  • 最后一句是什么使得这不是那么好`:(`否则是一个非常干净的实现. (6认同)

Alt*_*ife 20

这样做的一种巧妙的方法是将其置于一个函数中:

int yourfunc() {

    while(true) {

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

    }
}
Run Code Online (Sandbox Code Playgroud)

可选(但'不良做法'):如前所述,您可以使用goto,或在交换机内抛出异常.

  • 在这种情况下,抛出异常是非常邪恶的.这意味着"我想使用goto,但我读到了某个地方,我不应该使用它们,所以我会带着一个诡计,假装看起来很聪明". (13认同)
  • 应该使用异常来抛出一个异常,而不是一个熟悉的函数行为. (4认同)
  • 抛出异常就是用COMEFROM代替GOTO.不要这样做.goto工作得更好. (2认同)

Amb*_*ber 14

AFAIK在C++中没有"双重中断"或类似的构造.最接近的是goto- 虽然它的名称含义很差,但由于某种原因存在于语言中 - 只要它被谨慎使用,它就是一个可行的选择.


Ada*_*rce 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())
    ;
Run Code Online (Sandbox Code Playgroud)


Kir*_*sky 7

即使你不喜欢goto,也不要使用异常来退出循环.以下示例显示了它有多丑:

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

我会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;
    }
}
Run Code Online (Sandbox Code Playgroud)

甚至以下内容:

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

  • 使用异常来模拟goto当然比实际使用goto更糟糕!它可能也会明显变慢. (3认同)
  • @Downvoter:这是因为我说,你不应该使用异常,或者因为我建议重构? (2认同)

sha*_*oth 5

在这种情况下,没有用于打破循环的C++构造.

使用标志来中断循环或(如果适用)将代码提取到函数中并使用return.


Pra*_*ram 5

不,C++ 没有这方面的构造,因为关键字“break”已经被保留用于退出 switch 块。或者,带有退出标志的 do..while() 就足够了。

do { 
    switch(option){
        case 1: ..; break;
        ...
        case n: .. ;break;
        default: flag = false; break;
    }
} while(flag);
Run Code Online (Sandbox Code Playgroud)