目录
“循环”是一个非正式的术语,用来指代任意一种迭代控制结构 (iterativecontrol structure)——任一能够导致应用程序反复执行一段代码的结构。常见的循环种类有 C++和 Java 中的 for、while 和 do-while, 以及 Microsoft Visual Basic 中的For-Next、 While-Wend 和 Do-Loop-While。使用循环是编程中最复杂的方面之一;知道如何以及何时使用每一种循环是创建高质量软件的一个决定性因素。
在大多数语言中,你只能用到少数几种循环。
1、计数循环(counted loop)。执行的次数是一定的,可能是针对每位雇员执行一次。
2、连续求值的循环(continuously evaluated loop)。 预先并不知道将要执行多少次,它会在每次迭代时检查是否应该结束。例如,它会在还有剩余资金的时候继续运行,直到用户选择了退出,或者遇到了一个错误。
3、无限循环 (endless loop)。一旦启动就会一直执行下去。你会在心脏起搏器、微波炉以及导航控制仪等嵌入式系统里找到它。
4、迭代器循环(iterator loop)。对容器类里面的每个元素执行一次操作。
这些类型的循环首先是在灵活度上有差异——要么循环执行的次数是一定的,要么就在每次迭代的时候检查循环有没有完成。
这些循环还在检查循环是否执行完毕的位置上有所不同。你可以把检查放在循环的开始、中间或者结尾处。这一特征能够告诉你该循环会不会至少执行一次。
灵活度和检查位置决定了如何对用作控制结构的循环种类进行选择。表16-1显示了多种语言中的循环种类,并且描述了其灵活度和检查位置。

编程新手有时认为 while 循环会不断地求 while 条件的值,并且会在条件变成假的时候执行结束,而不论循环里面正在执行的是哪一条语句。尽管没有这些新手所想象的那么灵活,while 循环的确是一种灵活的循环选择。如果你预先并不知道循环要迭代多少次,那么就使用 while 循环。与一些编程新手所想的正相反,执行每通过这种循环一次,while 只做一次循环终止的检测,而且有关 while 循环的最主要事项就是决定在循环开始处还是结尾处做检测。
对于在开始处进行检测的循环,在 C++、C#、 Java、 Visual Basic 以及大多数其他的语言里,你可以使用 while 循环(括号内写明条件)。在其他语言里,你也可以模拟 while 循环。
你也许偶尔会遇到这种情况:需要一个灵活的循环,但是该循环至少需要执行一次。在这种情况下,你可以用一个在结尾处做条件检测的 while 循环。在C++、C#以及 Java 里,你可以用 do-while(括号内写明条件),在 Visual Basic 里用 Do-Loop-While,或者在其他语言里模拟在末尾进行条件检测的循环。
带退出的循环(Loop-with-exit)就是终止条件出现在循环中间而不是开始(while)或者末尾(do-while)的循环。Visual Basic 明确支持 loop-with-exit 循环,你可以在 C++、C 和 Java中用结构化的 while 和 break 来模拟它,或者在其他语言中用 goto 来模拟它。
一个带退出循环通常由循环头、循环体(包括终止条件)和循环尾组成,如下面的 Visual Basic 例子所示:

带退出循环通常是在这样的场合下使用:如果把循环条件检测放在循环开始或结束处,那就需要写出一个半循环 (loop-and-a-half) 的代码。下面是一个应该使用带退出循环但却没有用的 C++例子:

例子中的前两行代码又在 while 循环中的后两行中重复出现了。在修改的时候,你很容易忘记让这两组代码保持一致。如果别的程序员来修改这段代码,很可能不会注意到这两组代码应该同步地修改。不管怎样,其结果都是由于没有完全地修改而导致出错。你可以按照下面的样子来重写这些代码,以使之更加清晰:
在你使用这种循环的时候,请把下面这些细节考虑进去:
c++的goto示例。略
如果你需要一个执行次数固定的循环,那么 for 循环就是一个很好的选择。在C++、C、 Java、 Visual Basic 以及大多数其他语言里,你都可以使用它。
可以用 for 循环来执行那些不需要循环内部控制的简单操作。当循环控制就是简单的递增或递减,如对某一容器的元素进行迭代的时候,就可以使用 for 循环。for 循环的关键之处在于,你在循环头处把它写好后就可以忘掉它了,无须在循环的内部做任何事情去控制它。如果存在一个必须使执行从循环中跳出的条件,那么就应该改用 while 循环。
类似地,不要在 for 循环里通过直接修改下标值的方式迫使它终止,在这种情况下应该改用 while 循环。for 循环就是为了简单的用途,更复杂的循环最好用while 循环去处理。
foreach 循环或其等价物很适用于对数组或者其他容器的各项元素执行操作。它的优势在于消除了循环内务处理算术,从而也就消除了任何由循环控制算术导致出错的可能性。下面举一个这种循环的例子:

循环会出什么样的错误呢?任何一种答案都可以归结到下面所说的问题之一:
你可以采取两种方法来阻止这些错误的发生:

设法确认循环能够终止 。 这是基本要求。在脑海里模拟执行这个循环,直到你可以确认无论在何种情况下循环都能终止。要考虑到正常的情况、端点,以及每一种异常情况。
使循环终止条件看起来很明显。如果你使用 for 循环,而且没有随便修改循环下标,也没有使用 goto 或者 break 来跳出循环,那么这个循环的终止条件会是很明显的。与之类似,如果你在使用 while 或者 repeat-until 循环的时候把所有的控制语句都放在 while 子句或者 repeat-until 子句中,那么终止条件也会十分明显。关键就在于要把控制都放在一个地方。
考虑使用安全计数器。 安全计数器是一个变量,你在每次循环之后都递增它,以便判断该循环的执行次数是不是过多。如果你有一个程序,程序中发生的错误将带来灾难性的后果,那么就可以用安全计数器来确保所有的循环都终止了。下面的 C++循环将会因为使用安全计数器而受益:

安全计数器并不能包治百病。每次都在代码里使用一个安全计数器,也会增加复杂度,并且可能引发其他的错误。由于不是每个循环都要用它的,因此当你在使用了安全计数器的那部分程序中修改循环的时候,就可能会忘记维护安全计数器的相关代码。然而,如果把安全计数器作为整个项目的一种标准应用于关键的循环,那么含有安全计数器的代码不会比其他的代码更容易出错。
循环的长度可以用代码行数或者嵌套层次来衡量。下面给出一些指导原则:
循环要尽可能地短,以便能够一目了然。 如果你常常在显示器上看循环,而你的显示器能够显示 50 行代码,那么就应该把循环的长度限制在50 行以内。专家们曾建议把循环的长度限制在一页纸以内。不过,如果你开始接受编写简单代码这一原则,那就很少会写出超过15 或者20行的循环。
把嵌套限制在3层以内。 研究表明,当嵌套超出3层以后,程序员对循环的理解能力会极大地降低(Yourdon 1986a)。如果你写的嵌套层次超出了这一数字,那么应该通过把某一部分提取为子程序或者简化控制结构的方式来缩短它(在概念上)。
把长循环的内容移到子程序里 。如果循环设计得好,那么通常可以把循环体内的代码移到一个或几个子程序里面,并在循环体内加以调用。
要让长循环格外清晰。长度会增加复杂度。如果你写的是短循环,那么就可以使用 break、 continue、多个出口、复杂的终止条件等有风险的控制结构。如果你写的循环比较长,并且担心会给阅读者带来不便,那么就要给它写一个单一出口,并且要保持退出条件清晰无误。
略
略