Chapter 16 Controlling Loops
控制循环
“循环”是一个非正式的术语,用来指代任意一种迭代控制结构(iterative control structure)——任一能够导致应用程序反复执
行一段代码的结构。
16.1 Selecting the Kind of Loop
选择循环的种类
·计数循环(counted loop)执行的次数是一定的,可能是针对每位雇员执行一次。
·连续求值循环(continuously evaluation loop)
预先并不知道将要执行多少次,它会在每次迭代时检查是否应该结束。
·无限循环(endless loop) 一旦启动就会一直执行下去。你会在心脏起搏器,微波炉以及导航仪等嵌入式系统里找到它。
·迭代器循环(iterator loop)对容器类里面的每个元素执行一次操作。
·When to use a 'while' Loop 什么时候用’While‘循环
1. Loop with Test at the beginning 检测位于循环的开始
2. Loop with Test at the end 检测位于循环的结尾
·When to Use a Loop-With-Exit Loop 什么时候用带退出的循环
1. Normal Loop-with-exit Loops 正常的带退出的循环
带退出的循环也是单入单出的结构化控制结构,也是一种首选的循环控制。事实证明,他比其他种类的循环都要容易理
解。
2. Abnormal Loop-with-exit Loops 非正常的带退出的循环
即使用break退出的循环。
·When to Use a 'for' Loop 什么时候使用for循环
如果你需要一个执行次数固定的循环,那么for循环就是一个很好的选择。可以用for循环来执行那些不需要循环内部控制的简单操
作。如果存在一个必须使执行从循环中跳出的条件,那么就应该改用while循环。
不要在for循环中通过修改下标值的方式迫使它终止,在这种情况下应该改用while循环。for循环就是为了简单的用途,
更复杂的循环最好用while循环去处理。
·When to Use a 'foreach' Loop 什么时候使用foreach循环
foreach循环或其等价物(C#中的foreach,VB中的For-Each,以及Python中的for-in)很适用于对数组或者其他容器的各项元
素执行操作。它的优势在于消除了循环内务处理算术。从而也消除了任何由循环控制算法导致出错的可能性。
16.2 Controlling the Loop 循环控制
·Entering the Loop 进入循环
1. 只从一个位置进入循环
2. 把初始化代码紧放在循环前面
3. 用while(true)表示无限循环
有些程序员倾向于使用for(;;),这种也是可以接受的。
4. 在适当的情况下多使用for循环
因为在for循环中,所有相关的代码都写在循环的顶部,因此修改起来更加容易。如果能够很恰当地用for循环来替换其
他类型的循环,那就这么做。
·Processing the Middle of the Loop 处理好循环体
1. 用‘{’和‘}’把循环中的语句括起来 —— 防御性编程实践
2. 避免空循环
3. 把循环内务操作要么放在循环开始,要么放在循环的末尾
4. 一个循环只做一件事情
·Exiting the Loop 退出循环
1. 设法确认循环能够终止
2. 使循环终止条件看起来很明显。
3. 不要为了终止循环而胡乱改动for循环的下标
你可以改用While循环来获得对退出条件更多的控制权。
4. 避免出现依赖于循环下标最终取值的代码
在循环终止以后,使用循环下标是很不好的。
5. 考虑使用安全计数器
安全计数器是一个变量,你在每次循环之后都递增它,以便判断该循环的执行次数是不是过多。
但是,安全计数器并不能包治百病。每次都在代码里使用一个安全计数器,也会增加复杂度,并且可能引发其他的错误
。
· Exiting Loops Early 提前退出循环
很多语言都提供“除了满足for或者while的结束条件之外”的终止循环的方法。例如break,continue。
1. 考虑在while循环中使用break语句而不用布尔标记
通过往while循环中加入bool标记来实现退出循环体,可能使循环变得很难理解。用break来取代一系列的if检测,有时
候是你可以从循环中移除一些缩进层次,从而简化循环控制。
2. 小心那些有很多break散布其中的循环。
使用多个break不一定就是错误的,但是如果他们出现在循环中,那么就是一个警告的标记。它就像煤矿中的金丝雀,非但没有响
亮地歌唱,反而消耗那里的氧气。
3. 在循环开始处使用continue进行判断
一种使用continue的好方法是,在循环体开始处做完条件判断后让代码越过剩下的循环体继续执行。
这样使用continue,可以避免用一个能让整个循环体都缩进的if判断。反之,如果continue出现在循环中部或者尾部,
那么就应该改用if了。
4. 如果语言支持,请使用带标号break结构 (Java支持使用带标号的break语句(labeled break)。)
例如: break CALL_CENTER_DOWN;
5. 使用break和continue是要小心谨慎
除非你已经考虑过各种替换方案,否则不要使用break。我们无法确定continue和break是好是坏。其实这里的建议很
简单:如果你不能证明使用break或者continue的正当性,那么就不要用它们。
·Checking Endpoints 检查端点
对于一个简单的循环,通常需要注意三种情况:开始的情况,任意选择的中间情况,以及最终的情况。
你可以通过在头脑中模拟和手工运算而获益多多。这种智力训练带来的好处是:在最初的编码阶段少犯错误,在调试阶
段更快的找出错误,以及从整体上更好地理解应用程序。
·Using Loop Varables 使用循环变量
1. 用整数或者枚举类型表示数组和循环的边界
2. 在嵌套循环中使用有意义的变量名来提高其可读性
如果你用的是二位甚至多维数组,那么就应该用有意义的下标名来明确你的用意。
3. 用有意义的名字来避免循环下标串话。
习惯性地使用i,j,k可能会导致下标串话(cross-talk)
4. 把循环下标变量的作用域限制在本循环内。
·How long Should a loop be 循环应该有多长
1. 循环要尽可能地短,以便能够一目了然。
2. 把嵌套限制在3层以内。
研究表明,当嵌套超出3层之后,程序员对循环的理解能力会极大地降低(Yourdon 1986a)。
3. 把长的循环的内容移动到子程序里
4. 要让长循环格外清晰。
16.3 Creating Loops Easily-From the Inside Out 轻松创建循环--由内而外
16.4 Correspondence Between Loops and Arrays 循环和数组的关系
循环和数组有着密切的联系。很多情况中,循环是用来操纵数组的。而且循环计数器和数组下标是一一对应的。
KeyPoints 要点
·循环很复杂。保持循环简单将有助于别人阅读你的代码。
·保持循环简单的技巧包括:避免使用怪异的循环,减少嵌套层次,让入口和出口一目了然,把内务操作代码放在一处。
·循环下标很容易被滥用。因此命名要准确,并且要把它们各自仅用于一个用途。
·仔细地考虑循环,确认它在每一种情况下都运行正常,并且在所有可能的条件下都能退出。