循环结构
学习循环的工作原理以及何时使用它们。
控制流
之前我们学习了条件语句。它们用于管理程序的控制流。控制流指的是程序命令的执行顺序。
条件语句根据布尔表达式的计算结果选择不同的执行路径。然而,在某些情况下,仅靠条件语句还不够。我们需要额外的功能来更好地管理控制流。循环结构正是为此而设计的。
循环结构会重复执行同一段代码块多次。这段代码块的一次执行称为一次循环迭代。每次迭代后,循环都会检查其条件。根据检查结果决定是否继续下一次迭代。
命令的重复执行
为什么有人需要在程序中重复执行相同的代码块?让我们通过几个例子来回答这个问题。
我们已经熟悉了 find 工具(来自之前的课程)。它用于在磁盘上查找文件和目录。如果在 find 命令中添加 -exec 选项,就可以指定一个操作。该工具会对每个找到的对象执行这个操作。
例如,以下命令列出 ~/Documents 目录下的所有 PDF 文件:
1find ~/Documents -name "*.pdf" -exec ls -al {} \;
请在下方终端中运行本课讨论的命令。
在这个例子中,find 多次调用 ls 工具,每次传递一个找到的文件。这意味着 find 隐式地执行了一个循环。当 find 完成对所有找到文件的处理后,循环结束。
另一个命令重复的例子是 du 工具。该工具用于估算占用的磁盘空间。它有一个可选参数,用于设置开始估算的路径。
以下是 du 的使用示例:
1du ~/
在此情况下,工具递归遍历用户的主目录子目录,并将每个找到的文件大小累加到最终结果中。这样,结果的不断累加实际上就是多次重复执行相同操作。
du 工具也隐式地执行了一个循环。该循环遍历所有文件和子目录,并在每次迭代中执行相同的操作。不同之处仅在于当前检查的文件系统对象。
在常规数学计算中,我们也经常遇到操作重复的情况。一个经典例子是计算阶乘。一个数 N 的阶乘是 1 到 N(包含 N)之间所有自然数的乘积。
例如,计算 4 的阶乘:
14! = 1 * 2 * 3 * 4 = 24
使用循环可以轻松实现阶乘计算。循环应按顺序遍历从 1 到 N 的整数,将最终结果与每个经过的整数相乘。这样,乘法操作就被重复执行多次。
最后一个计算机系统中操作重复的例子是:重复是一种有效的方式来处理某些事件。
假设我们编写一个程序,从互联网下载文件到本地计算机。首先,程序建立与服务器的连接。如果服务器没有响应,程序有两个选择:一是以非零退出码终止;二是等待服务器响应。显然,第二种选择更合理。
网络包延迟的原因有很多,比如网络过载。等待几秒钟可能就足以收到数据包。此时,程序可以继续正常运行。
现在的问题是:如何在程序中等待某个事件发生?最简单的方法就是使用循环。循环的条件应检查事件是否发生,一旦发生,循环就停止。
回到我们的例子:循环应在程序接收到服务器响应时停止。在此期间,循环持续运行。我们不需要在每次迭代中执行任何具体操作,因此可以将循环体留空。这种技术称为忙等待。
忙等待虽然不执行任何任务,但会大量消耗 CPU 时间。因此,我们应尽可能优化它。一种优化方法是在循环体中加入一条让程序短暂休眠的命令。这可以让操作系统有机会执行其他任务,从而提升整体效率。
重复解决的问题
我们已经看到程序多次重复执行相同操作的多种场景。总结一下,重复主要用于:
-
反复处理多个实体。例如
find工具就是逐个处理搜索结果。 -
累积中间数据以计算最终结果。例如
du工具通过累加文件大小来生成统计信息。 -
数学计算。例如使用循环计算阶乘。
-
等待某个事件发生。例如在忙等待循环中等待服务器响应。
这份清单远未穷尽。它仅展示了最常见且需要循环结构支持的编程任务。