【问题标题】:Why use a for loop instead of a while loop? [duplicate]为什么使用 for 循环而不是 while 循环? [复制]
【发布时间】:2011-04-21 23:11:29
【问题描述】:

可能的重复:
Iterate with for loop or while loop?
Loops in C - for() or while() - which is BEST?

什么时候应该使用for 循环而不是while 循环?

我认为以下循环是相同的,除了它们的语法。如果是这样,那为什么要选择一个而不是另一个?

int i;
for (i = 0; i < arr.length; i++) {
  // do work
}

int i = 0;
while (i < arr.length) {
  // do work
  i++;
}

【问题讨论】:

  • while循环也可以是while($var=function($parameter)){}
  • 您是否针对此问题所针对的语言?
  • @贾斯汀:+1。我已经很多年没有使用循环了,我的语言有各种方法。
  • @Matt,那是什么语言?
  • @贾斯汀:红宝石。任何混合在 Enumerable (几乎任何集合)中的东西都会有一个 each 方法,它接受一个块,并将集合中的每个项目都生成给它。当您需要数字索引时,还有 each_with_index 。该语言具有 foreach/for 作为更传统的控制语法,但我认为我从未见过它们使用过(在书籍之外)

标签: loops coding-style control-flow


【解决方案1】:

在您的情况下,除了在for 循环中少了一行代码之外,您并没有什么收获。

但是,如果你这样声明循环:

for(int i = 0; i < x; i++)

您设法将i 保持在循环范围内,而不是让它逃逸到代码的其余部分。

另外,在 while 循环中,变量声明、条件和增量位于 3 个不同的位置。使用 for 循环,所有内容都集中在一个方便、易于阅读的地方。

最后的想法:
还有一个重要的注意事项。两者之间存在语义差异。一般来说,While 循环意味着有无限次迭代。 (即,直到文件被读取......不管它有多少行),并且 for 循环应该有更明确的迭代次数。 (循环遍历集合中的所有元素,我们可以根据集合的大小来计算。)

【讨论】:

  • ... 在某些语言中。在其他情况下,不是。
  • +1 我提到了计数器变量的范围。
  • @Ziggy:一般来说,较高的数据局部性是好的。在这种情况下,一个优点是您可以在同一范围内的不同循环中使用相同的计数器变量名称(经典的i)。此外,如果计数器变量“继续存在”在它们的循环后面,它们可能会被“回收”以供以后的代码使用,这可能会意外地依赖于它们具有特定值作为循环的剩余部分。我想一个人可以想出无数这样的场景。
  • @Ziggy 好吧,假设您有多个循环。使用 for 循环,所有循环计数器都位于循环的本地。您可以在循环中使用通用名称(如“i”),而不必担心声明与已存在的变量同名的变量。
  • @sbi:您可以在多个循环中重用变量名,无论您是在内部还是外部声明它。在后一种情况下,您正在重用变量本身。问题不在于它不可能,而是它有风险,因为您可能会忘记重新初始化它。
【解决方案2】:

选择for 而不是while 的原因之一:可读性。

通过使用for 循环,您明确表示您的意图是执行某种需要初始化、“步骤”操作和完成条件的操作。

另一方面,对于while,你只是说你需要完成条件。

【讨论】:

  • 真的只有一个的原因吗?我在下面提到的另一个呢?
  • @Justin:我没有说“只有一个”——但坦率地说,我认为你的理由主要是可读性……
  • @Reed,碰一下。它主要是可读性,真的。
  • 我认为当时的初始化对我来说最重要。 for(int i = 0;int j = 0;;)
  • @Justin/Reed:我认为 Justin 的替代方案不仅有利于可读性。将i 保持在循环范围内的事实是一件好事。有助于保持代码干净!
【解决方案3】:

当我不一定需要计数器时,我使用“while”,例如当您从文件中读取并等待到达 EOF 时。在这种情况下,'for' 可能不是最好的选择。当您浏览数组中的项目时,我宁愿使用“for”,因为它可以更好地传达意图。

【讨论】:

  • 是的,对变量使用 while 是 counter 直观的。
  • +1,真实实用。
【解决方案4】:

for() 和 while() 循环之间的一个显着区别是 while() 循环中的“继续”语句将分支到循环的顶部,而 for() 循环中的语句将分支到for() 子句的第三部分[条件后面的部分,通常用于碰撞变量]。

【讨论】:

    【解决方案5】:

    作为一名老师,我一直在以各种形式思考这个问题;我的建议总是归结为

    • for-loops 用于计数。向上计数,向下计数。
    • while / do-while 构造适用于所有其他条件。 c!=EOFdiff&lt;0.02等 迭代器/枚举器是非常适合 for 循环的计数器。

    即你的int=0; while( ... ) 在我眼里很丑。

    【讨论】:

      【解决方案6】:

      为什么选择可乐而不是百事可乐?

      在 WHILE 和 FOR 循环之间,您可以互换使用它们。要成为纯粹主义者,您可以根据条件的性质做出决定。如果您正在执行基于计数的循环,那么 FOR 循环将是最有意义的。

      for( cur = 0; cur < myList.Length; cur++ ){
          doSomething( myList[cur] );
      }
      

      如果您正在执行基于逻辑的循环,那么 WHILE 将是最干净的实现

      Iterator i = myObject.getIterator();
      while( i.hasNext() ){
          doSomething( i.next() );
      }
      

      【讨论】:

      • 我认为这是我想听到的答案(一个很好的理由不马上接受它)。人们普遍同意你的观点吗?
      • 我仍然认为使用迭代器时 for-loop 仍然是更清洁的实现:for (Iterator i = myObject.getIterator(); i.hasNext(); i.next()); for-loop 的迭代器版本看起来与使用 for-int-looping 非常相似。当您想强调循环在达到某个条件时停止时,while-loop 更清晰,并且停止条件既不是由循环计数器也不是由迭代器确定(例如 GUI 主循环,终止条件是按下退出按钮时) .
      • 所有的事情都说了和做了,这可以归结为个人喜好。我认为这里提供的任何答案都没有错。
      • @Lie Ryan:问题在于,在像 Java 这样具有这种迭代器风格的语言中,i.next() 通常返回元素——没有办法在不推进迭代器的情况下访问元素。 (当然,你用foreach代替。C++迭代器没有这个问题,for自然存在。)
      【解决方案7】:

      正如你所说,原因是语义和美学,而不是性能(它们编译的代码往往非常相似)。

      也就是说,for 通常在具有已知迭代次数的循环中,而while 暗示该条件不一定与您在循环中所做的事情相关,或者您不相关一开始就知道会有多少次迭代。

      【讨论】:

      • 我想这就是我偷偷问的问题。例如,在线性搜索中,根据定义,我不确定需要多长时间,然后你会在 for 循环上使用 while 循环吗?
      • @Ziggy:啊,这主要取决于你的意图。如果我正在搜索一个已知维度的结构,我会使用for,即使我在找到匹配项时可能不得不打破它。另一方面,如果我在没有已知大小的流中搜索,我会使用while
      【解决方案8】:

      通常,我总是更喜欢 for 循环,即使对于循环与计数无关的情况,因为它将循环的维护本地化在一个位置。当其他人出现并决定添加/修改循环时,如果他们使用的是 FOR 循环,那么进行修改的人通常可以确信,只要他们不弄乱 FOR 表达式中的项目,它们不会破坏循环中的迭代。

      但是,像 while 循环这样的东西需要程序员完全解析循环体,并了解它是如何迭代的,以确保在进行更改时保持循环维护代码未修改且顺序正确。

      因此,我在我只有效地需要 FOR 的中间部分的情况下使用 WHILE。

      【讨论】:

        【解决方案9】:

        它们在功能上是相同的,但是可以说 for 循环不太容易出错,因为所有循环功能都在一起。如果你有几行分隔 i 的声明、while 声明和索引迭代器,你可能会忘记它,或者对它的使用方式感到困惑。当然,这都是主观的。

        【讨论】:

          【解决方案10】:

          在编程的早期,流行的数据结构是线性数组(又名向量),而重复模式是遍历和使用数组的每个元素。这种情况很常见,语言包含这种模式的特定语法:for循环。除了这种历史遗产之外,您还可以使用 while 循环(现在使用 F# 之类的函数式语言,您宁愿使用 List.map 或 List.fold ;-)

          【讨论】:

            【解决方案11】:

            这主要是可读性问题。通常,当您有定义的值范围进行迭代时,请使用 for 循环。在不迭代或不知道退出条件何时为真时使用 while 循环。

            从 java 的角度来看,我发现 for 循环对于集合来说非常方便:

            for (Object o : objects){
                o.process();
            }
            

            比阅读更容易:

            int i=0;
            while(i < objects.size()){
                objects.get(i).process();
                i++;
            }
            

            【讨论】:

            • 在while循环中,也可以使用迭代器。 while(iterator.hasNext()) iterator.next().process.
            • 第一个例子在技术上是一个 for-each 结构
            【解决方案12】:

            它们通常会编译成相同的最终结果。大多数时候,我更喜欢 for's 或 for-each。写作

            for (;x<5;x++){ 
                //do work 
            } 
            

            不是那么险恶,而是写作

            while(x<5){
                x++
                //do work
            }
            

            而不是

            while(x<5){
                //do work
                x++
            }
            

            可能会使某些人感到困惑并导致错误。

            【讨论】:

              【解决方案13】:

              在像 Pascal 这样的语言中,for 和 while 结构有不同的应用。您将在 Pascal 中编写一个 for 循环,如下所示:

              for i := 1 to 5 do
              begin
              { some code here }
              end
              

              所以你明确声明 i 增加 1。这里没有明确指定终止条件。因此,您只能在您知道循环将在实际循环执行开始之前运行特定预定次数的地方使用 for 循环。另一方面,您可以在 while 循环的情况下指定显式终止条件。

              i:=0;
              while (i<>5) do
              begin
              { some code here }
              i:=i+1;
              end
              

              因此,while 循环更加灵活。后来开发 C 时,虽然支持 for 和 while,但 for 构造的含义发生了变化,您可以使用布尔运算符添加显式终止条件。因此,while 和 for 在 C 和类 C 语言中的功能是相同的,因此它更多地成为可读性问题而不是可用性问题。

              【讨论】:

                【解决方案14】:

                浏览其他(非常好的)答案,有一点我没有看到。 (如果真的成功了,我会错过并道歉。)

                我将给你两个看似语义相同的sn-ps代码。

                片段#1:

                for (int a = 0; a < arr.length; a++) {
                  /* do some work here */
                }
                

                片段#2:

                int b = 0;
                while (b < arr.length) {
                  // do work
                  b++;
                }
                

                他们看起来好像在做同样的事情,对吧?现在让我在每个 sn-p 中添加一行,让我们看看会发生什么:

                片段#3:

                for (int c = 0; c < arr.length; c++) {
                  /* do some work here */
                }
                printf("%d\n", c);
                

                片段#4:

                int d = 0;
                while (d < arr.length) {
                  // do work
                  d++;
                }
                printf("%d\n", d);
                

                因此,当我编译一个嵌入了这四个 sn-ps 的垃圾文件(加上一些胶水以使 arr.length 有意义)时,我收到以下错误:

                $ clang junk.c
                junk.c:20:17: error: use of undeclared identifier 'c'
                        printf("%d\n", c);
                                       ^
                1 diagnostic generated.
                

                使用for 循环,事实证明,允许您为虚拟计数器变量等提供更多的局部性。这使您在重用变量名时有更大的余地而不会发生冲突(尽管以增加潜在的阴影为代价)。

                【讨论】:

                  【解决方案15】:

                  一般来说,while 循环和 for 循环之间的区别在于语法而非性能。也就是说,看起来这个问题已经为 Java 解决了:Java for loop vs. while loop. Performance difference?

                  【讨论】:

                  • 哈哈,我正要去那个链接看看结果是什么……然后我找到了答案。感谢您的链接和回忆。
                  • 很高兴我能照亮你的一天!
                  【解决方案16】:

                  可读性。而从老VB 6天的wend,每当看到一会想哭。

                  在某些情况下,while 循环很好用。在对象上调用一个函数,该函数返回 true/false,但在该对象内迭代一个值。

                  C# 中的 DataReader 示例:

                  List<string> strings = new List<string>();
                  while(DataReader.Read())
                  {
                    strings.Add(DataReader["foo"].ToString());
                  }
                  

                  for 循环允许您将工作封装在一个函数中,从而提供更好的内存管理。此外,如果您的 while 循环中有错误,它可能会演变成一个失控的循环,威胁到所有人的生命:

                  bool keepGoing = true;
                  while(keepgoing)
                  {
                     try
                     {
                       //something here that causes exception, fails to set keepGoing = false;
                       keepGoing = false;
                     }
                     catch(Exception)
                     {
                      //Do something, but forget to set keepGoing.
                      //maybe there is always an exception...
                     }
                  
                  }
                  

                  我也认为这只是一般做法。很像在建筑中。您使用钉子将屋顶平铺到螺柱上。你“可以”使用螺丝,它们会做同样的工作,但这不是常见的做法。

                  没有人说这是错的,我们只是说“请在同行的压力下放弃,以保持编码世界的正常运转”。

                  【讨论】:

                  • "wend" 早于 VB6 - 我第一次接触它是在 Fortran 中。
                  【解决方案17】:

                  forwhile 循环之间存在语义差异。使用for 循环,迭代次数是预先定义的,而使用while 循环则没有必要。

                  某些语言(例如 Ada)通过将 for 循环计数器设置为循环体中的常量来强制执行此操作。因此,这样说是违法的,例如:

                  for i in values'range loop
                     ...
                     i := i + 1;   -- this is illegal, as i is a constant within the loop body
                  end if;
                  

                  然而,这种语义差异在大多数语言中并不那么明显,它们不将for 循环计数器视为常量。

                  【讨论】:

                  • 很多语言在 for 中甚至根本不需要计数器,for(;cond;) { ... } 与 C 中的 while(cond) { ... } 和以 C++ 为例。
                  • @Logan:没错——这就是为什么大多数人不关心语义差异的原因,因为 for 循环只是 while 循环的语法糖。
                  【解决方案18】:

                  只需决定一个,然后在整个项目中复制和粘贴/调整它。更少的决定,更少的打字,更快的编码。性能差异可以忽略不计。

                  我也不同意 Justin 将 i 保持在循环范围内,我使用 var aL = array.length 也在循环之外。然后在for 循环构造函数中使用iaL,这样您就不会在每次命中时重新计算数组的长度(以及一遍又一遍地创建var;))。再次否定。性能真的很好(见我的回答的第一部分),除非你有非常大的阵列飞来飞去。如果您碰巧事先知道数组长度,请先使用其固定长度启动它(在蹩脚的浏览器上对内存分配有一点帮助;)如果这是客户端并且您有很多数据)。

                  js 示例

                  <script>
                  var i=0;
                  var myArray = ["D","R","Y"];//literal style will do for now;)
                  var aL=myArray.length; //or use 3 if known upfront
                  for(i;i<aL;i++){/*always do DRY things here*/}
                  </script>
                  

                  还要确保满足条件时,问问自己“我还需要完成整个循环吗?”如果没有,请不要忘记尽早跳出循环。

                  【讨论】:

                  • 除非您打算在循环结束后使用它,否则您不需要在循环范围之外初始化 i。数组长度 hack 仅对非常大的数组有意义 - 对于几次迭代,我会坚持使用更简洁的代码。
                  【解决方案19】:

                  相关琐事:

                  在 Google 的新 Go language 中,他们决定不包含 while 语句。相反,它们允许您编写一个只有条件子句的for statement。所以相当于

                  while (a < b) { ... }
                  

                  很简单

                  for a < b { ... }
                  

                  【讨论】:

                    【解决方案20】:

                    简单地说:

                    “for”循环倾向于用于迭代使用。您需要遍历一个数组,出于各种原因,您需要从 1 到 100(或其他范围)的数字。

                    “while”循环往往是条件循环。你想继续做某事,直到一个 contition 是错误的。假设您有一个名为“Toast”的类,并且您想将吐司煮至烤好,您将得到如下内容:

                    while (!toast.isToasted) cook(toast);

                    你几乎总是可以(我想不出一个你不能的例子)写一个 for 循环作为一个 while 循环和一个 while 循环作为一个 for 循环,但是选择最先出现情况的那个归结为我以上几点。

                    【讨论】:

                      【解决方案21】:

                      可读性是非常重要的一点,但它是双向的。我会将 for 循环转换为 while 循环——即使我使用了一个计数器变量,否则使用 for 是“自然的”——这样更易读。当您在 for 的三个部分或多个计数器中的任何一个中有一个长或复杂的表达式时,这很常见(这与复杂有关)。

                      Knuth's loop-and-a-half 在多种语言中也能更好地作为带中断的无限循环而不是 for 循环。

                      但是,有一个非常重要的语义区别:

                      for (int i = 0; i < arr.length; i++) {
                        if (filter(arr[i])) continue;
                        use(arr[i]);
                      }
                      
                      int i = 0;
                      while (i < arr.length) {
                        if (filter(arr[i])) continue;
                        use(arr[i]);
                        i++;
                      }
                      

                      【讨论】:

                        【解决方案22】:

                        在适当的时候使用 for 循环。 while 解决方案有语法缺陷(更长),您必须在循环中的任何 continue 调用之前检查是否增加 i。

                        【讨论】:

                          【解决方案23】:

                          偏好。对我来说,所有循环逻辑都内置在 for 语句中,因此您不必声明计数器变量或在实际循环中执行递增/递减操作。此外,您可以在 for 语句中声明计数器/索引。

                          【讨论】:

                          【解决方案24】:

                          For 循环通常更容易并行化。由于程序可以提前知道将要执行的迭代次数以及迭代器在任何时候的值,因此使用 OpenMP 等 API 进行并行化是微不足道的。当然,如果你手动中断、继续和更改迭代器,这个优势就会消失。

                          【讨论】:

                            【解决方案25】:

                            因为后者可以是一个班轮:while (++i

                            【讨论】:

                              【解决方案26】:

                              一个原因是while的条件可以是一个字符串。

                              此外,如果我们将“Do”添加到组合中,当我们希望操作至少运行一次时,无论其当前状态如何,我们都可以使用 do while 循环。

                              【讨论】:

                                【解决方案27】:

                                这只是我的逻辑,当我们知道迭代的确切计数时,我们通常使用 for 循环,而当我们通常想要检查条件时,我们使用 while 循环。然后我们根据您的要求使用它们。否则,它们都会表现出相同的行为。

                                【讨论】:

                                  【解决方案28】:

                                  一个while循环,定义为

                                  while(condition)
                                  

                                  代表这种编程模式:

                                  1:
                                  if(!condition) goto 2;
                                  ...
                                  goto 1;
                                  2:
                                  

                                  而 for 循环,定义为

                                  for(var i = seed; condition; operation)
                                  

                                  代表这种模式:

                                  var i = seed
                                  1:
                                  if(!condition) goto 2;
                                  ...
                                  operation;
                                  goto 1;
                                  2:
                                  

                                  这两种模式都非常常用,以至于它们已被命名并内置于语言中。

                                  如果您需要定义一个计数器来控制您的循环,请使用 for 循环。如果没有,请使用 while 循环。

                                  【讨论】:

                                    猜你喜欢
                                    • 2011-10-06
                                    • 2012-10-28
                                    • 1970-01-01
                                    • 2020-08-14
                                    • 1970-01-01
                                    • 2018-07-05
                                    • 1970-01-01
                                    • 2020-12-10
                                    • 1970-01-01
                                    相关资源
                                    最近更新 更多