while 循环
R 中的 while 循环只要条件保持为 TRUE,就会一直执行循环体。for 循环对某个集合的每个元素各执行一次,而 while 执行的次数是未知的,直到某个条件发生变化:
每个 while 循环都由三部分组成,这里三部分都清晰可见:
- 循环前的准备:
count <- 1让条件有东西可以检测。 - 条件:
count <= 5,由常用的比较运算符构成,每一轮之前都会检查。 - 循环体中的更新:
count <- count + 1让循环逐步走向结束。
删掉第 3 部分,条件就会永远为 TRUE:循环永远不会结束。这是 while 最典型的风险,本页其余内容大部分都在讲如何控制它。
每一轮之前检查条件
while 先检测条件,再执行循环体。这带来两个结果。如果一开始条件就是 FALSE,循环体会执行零次:
另外,检查发生在每一轮完整执行之间,而不是在循环体中途:一轮一旦开始,即使条件在中途变成了 FALSE,也会执行到底。如果你需要在循环体中途退出,那正是 break 的用途(见下文)。
示例:不断减半直到低于阈值
while 最适合“一直做,直到……”的场景:你无法预先知道需要多少轮,所以 for 不合适。1000 要减半多少次才会小于 1?
减半十次,最终约为 0.977。这种模式(循环体不断缩小一个状态变量,同时增大一个计数器)涵盖了现实中很大一部分 while 循环:一直运行到误差足够小的数值方法,一直运行到资源耗尽的模拟,以及一直重试到调用成功的重试逻辑。
repeat + break:R 的 do-while
R 没有 do-while 关键字,也就是没有在循环体之后检测条件的循环。但它有 repeat,会无条件地永远循环下去,还有 break,可以立即退出任何循环。把 break 判断放在 repeat 循环体的末尾,就得到了一个标准的 do-while:循环体至少保证执行一次:
循环体总是在第一次判断之前执行,所以 n 至少会翻倍一次;循环在 128 时退出,也就是第一次超过 100 的翻倍结果。当“先做这件事,再决定要不要再做一次”是对需求最贴切的描述时,就用这种结构,比如反复读取输入直到合法,反复抽样直到符合条件。没有可达 break 的 repeat 天生就是死循环,所以先写好 break,再填充循环体。
break 在 while 和 for 循环中的作用完全相同,它是通用的“到此为止”出口,很自然地与在循环体中途检测条件的 if 语句搭配使用。
while 循环中的 next
next 跳过当前这一轮的剩余部分,直接回到条件检查。它在 while 中的作用和在 for 中一样,但有一个 while 特有的陷阱:如果更新步骤写在 next 之后,跳过它就意味着循环变量永远不会前进,你就写出了一个死循环。把更新写在 next 之前:
这段代码打印 2 到 10 之间的偶数。如果把 i <- i + 1 移到 next 那一行下面,遇到第一个奇数就会永远跳过更新。for 循环不存在这个隐患(循环变量会自动前进),这也是在单纯遍历序列时更推荐用 for 的又一个原因。
防止死循环
所有死循环都是同一种 bug:循环体中没有任何操作改变条件所依赖的变量,或者改变的方向不对。在交互环境中你可以按 Esc 或 Ctrl+C 中断,但在脚本中,进程只会卡住。有两个习惯可以避免这种情况。
第一,写条件时,立刻问自己:“循环体中哪一行会让它朝 FALSE 前进?”并确保这一行在循环体的每一条执行路径上都会运行(注意上面提到的 next 陷阱)。
第二,对于难以证明一定会结束的循环,加上最大迭代次数的保护,这样逻辑 bug 只会产生一个可以诊断的结果,而不会让会话卡死。下面用它来保护一个类似考拉兹猜想的循环:偶数反复减半,奇数 x 变成 3 * x + 1,直到得到 1:
从 10 开始,循环在 6 步后得到 1。如果某个起始值永远得不到 1,steps < 10000 这个条件仍会结束循环,打印出的状态会告诉你哪里出了问题:一个看得见的错误答案,总比一个永远不给答案的程序强。这里用短路运算符 && 是正确的:两边都是单个值,整个保护条件读起来就像一句话。
while 还是 for:如何选择
经验法则取决于你事先知道什么:
- 你知道要遍历的是什么(向量的元素、1 到 n 的数字):用 for 循环,循环变量会自行管理。
- 你只知道停止条件(直到收敛、直到低于阈值、直到合法):用
while。 - 循环体必须先执行至少一次,判断才有意义:用
repeat加一个终止用的break。
如果你发现自己在 while 循环里手动维护一个计数器,只是为了遍历一个向量,那其实是一个伪装起来的 for 循环,换掉它吧。
本节要点
while (condition) { ... }先检查再执行,一开始条件就为假则执行零轮。- 循环体必须让条件朝
FALSE前进;准备、条件、更新这三件套就是全部的纪律。 - R 没有 do-while 关键字:惯用写法是
repeat { body; if (done) break },保证至少执行一轮。 break可以退出任何循环;在while中使用next时,要在跳过之前更新循环变量,否则它永远不会前进。- 最大迭代次数保护(
&& steps < 10000)能把潜在的卡死变成一个看得见、可调试的错误答案。
下一节:向量,本页每个循环一直在遍历的数据结构,也是很多 R 循环可以完全消失的原因。
常见问题
R 中的 while 循环是如何工作的?
while (condition) { ... } 先检查条件,只有在条件保持为 TRUE 时才执行循环体。循环体中必须有某个操作最终让条件变为 FALSE,通常是更新计数器或缩小某个值,否则循环永远不会结束。如果一开始条件就是 FALSE,循环体一次也不会执行。
R 有 do-while 循环吗?
没有这个关键字。惯用写法是 repeat { ...body...; if (condition) break }:repeat 无条件循环,所以循环体至少会执行一次,末尾的 break 则充当 do-while 的退出判断。
R 中如何停止死循环?
在交互环境中,按 Esc(RStudio)或 Ctrl+C(终端)中断。要从源头上避免:确保循环体每一轮都会更新条件中的变量,并加上最大迭代次数的保护,比如 while (working && steps < 10000),这样逻辑 bug 只会产生一个错误的计数,而不会让会话卡死。