【问题标题】:Use for as while with counter与计数器一起使用
【发布时间】:2014-10-22 20:40:36
【问题描述】:

大多数时候,当我们检查在 while 循环体中发生的事件时会使用 while 循环。当给出“确定的”迭代次数时,通常使用 for。让我们假设我们需要知道我们正在进行的迭代次数,一般来说:

一般的 While 循环:

bool flag = false;
int it = 0;
while (!flag)
{ 
    //... do something using it
    ++it;
    flag = getFlag();
}

通用For循环,迭代次数是循环固有的:

for(int it = 0; it < N; ++it)
{
    //... do something using it
    if (getFlag())
        break;
}

但是,对于未设置迭代次数的情况(即,我们一直循环直到偶数发生)我从未见过类似的情况:

for(int it = 0; !getFlag(); ++it)
{
    //...do something using it
}

但我总是看到类似于我编写的第一个 while 循环的内容。有什么真正的区别吗? while循环是否被认为是更好的样式?因为对我来说,for 循环更紧凑,更易于阅读和遵循。

【问题讨论】:

  • 这是正确的,但对于只习惯于看到for 以标准“递增到某个限制”方式使用的人来说,这可能会让人感到惊讶。
  • @dlf "是正确的,但仅限于检查终止条件是在开始下一次迭代之前执行的最后一步的特定情况" -- 同时并且 for 循环在这种行为中是相同的,所以我不确定你的意思是什么。
  • for 几乎被定义为在编译时转换为 while 循环。
  • @dlf 我也不明白这一点(如先前的评论所述)。我已经修复了添加!的代码

标签: c++ for-loop while-loop


【解决方案1】:

通常你应该使用你认为最易读的那个,但要注意这两种结构之间的区别可能会让你措手不及:continue 的行为。

continue 不会使用 while 构造增加 it,但会使用 for 构造增加它。

while (!getFlag()) {
    // ...

    if (something) {
        // "it" won't be incremented!
        continue;
    }

    // ...

    ++it;
}

for(int it = 0; !getFlag(); ++it) {
    // ...

    if (something) {
        // "it" will be incremented!
        continue;
    }

    // ...
}

如果您不使用continue,那么行为将是相同的,但是您不能做出一般性声明for (a; b; c) { d; } 完全等同于{ a; while (b) { d; c; } },除非您添加限制d 不能包含continue 声明。

【讨论】:

    【解决方案2】:

    您描述的 while() 和 for() 循环在执行上没有区别。没有一个更好或更优雅的标准或通用约定。坚持使用你觉得更舒服的那个。

    【讨论】:

      【解决方案3】:

      有什么真正的区别吗?

      没有。除了变量作用域和continue 的含义之外,这些完全等价:

      initialize;
      while (condition) {
        body;
        increment;
      }
      
      for (initialize; condition; increment) {
        body;
      }
      

      while 循环是否被认为是更好的样式?

      正如您所提到的,每种类型的循环都有典型的用例:

      • 如果您有一些初始化、测试和增量,请使用for 循环。也不一定是已知的迭代次数;一个典型的 C++ 模式是在 i != some_container.end() 时进行迭代。我认为您的最后一个示例作为for 循环运行良好。

      • 如果您不需要初始化和递增,请使用while 循环。写for (; condition;) 很傻。

      但是,存在灰色区域,其中任一类型的循环都有意义。这主要是个人喜好问题。

      【讨论】:

      • 不,它们不等价。如果body 包含continue,那么increment 将在while 情况下被跳过,但在for 情况下不会被跳过。
      【解决方案4】:

      以上所有情况都是有效的,并且在我的书中完全可以(假设您打算在第一种情况下设置flag = true;,或者至少不是恒定的false,因为它永远不会进入循环)。

      我有时会使用:

       for(int i = 0; ; i++)
       {
          ...
          if (getFlag()) break;
          ... 
       }
      

      另一个最喜欢的是典型的“链表循环”:

      for(Node* p = head; p; p = p->next)
      {
          ... Do stuff with p ... 
      }
      

      在很大程度上,这取决于你实际上想要表达什么......

      编译器很可能会为所有三个生成相同或至少非常相似的代码。

      【讨论】:

      • 我在示例代码中有一个关于标志的错误,我已经更正了。
      • 是的,我认为是这样。顺便说一句,如果getFlag() 函数在最后一个示例中的第一次调用时返回false,它也不会进入循环(与我的第二个示例中的空列表相同)。
      【解决方案5】:

      事实是

      for (exp1; exp2; exp3) {
         statements;
      }
      

      等价于

      {   // I put the brace, because exp1 may be a declaration of variable local to the loop
         exp1; 
         while (exp2) {
            statements;  // as long as there is no continue (or if you replace every continue; with { exp3; continue; } 
            exp3; 
         }
      }
      

      所以这是一个品味问题,一个循环特定的初始化和一个增量表达式,以及使用continue statements 的方式。

      但是您的分析是准确的:for 经常用于迭代,如 c++11 range-for 所示:

      for (x : container) {
         statements; 
      }
      

      原因(至少根据 B.Stroustrup 的说法)是迭代的​​所有控制逻辑都可以轻松地重新组合到循环顶部的一个可见位置。

      【讨论】:

      • 不,它们不等价。如果statements 包含continue,那么exp3 将在while 情况下被跳过,但在for 情况下不会被跳过。
      • @cdhowie 你当然是完全正确的!但这是否改变了它基本上是相同的构造和品味问题的一般说法?
      • 是的,有点——在某些情况下,您会更喜欢其中一种,不是因为品味,而是因为您需要使用continue,并且需要一种或另一种行为。
      • 但是continue 本身不是品味和风格的问题吗?您始终可以将有条件的 continue 构造重写为没有 continue 的条件构造。但我们继续讨论它的事实表明,这个问题主要是基于意见的,我很确定我们俩肯定会以非常相似的方式选择 while 或 for ;-)
      • 在一定程度上,当然……我的意思是,使用语言的任何图灵完备子集,我们都可以模拟任何其他图灵完备子集,对吧?谁需要上课? ;)
      【解决方案6】:

      虽然我同意大多数人的观点,但没有大的区别(事实上在大多数情况下,它们实际上会编译成相同的代码)。我总是被教导不要在循环结束时内部操作(迭代器除外)可能发生变化的情况下使用 for 循环。这是为了可读性。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-23
        • 1970-01-01
        • 2017-06-01
        • 1970-01-01
        • 2021-01-12
        相关资源
        最近更新 更多