学径 LearnPath

编程语言基础

循环

把人无法耐心完成的重复交给计算机。

讲解模式

通过本节课,你将理解:

#goal

程序除了可以向下跳过一些行,也可以向上跳回一些行,使得这些行被多次重复地执行。

这就是 循环结构,在 Python 里通过 while 或 for...in... 以及缩进来描述。

为了避免程序陷入无尽的循环,程序中通常要有对继续或结束循环的判断条件。

本节课你将学会:如何写出循环结构,以及如何提前结束一个循环。

回到「过去」

#hook

使用电热水壶烧开水,水壶接到指令之后,程序其实是反复做同一件事:

  • 测到的水温还没到设定温度:继续加热,然后再测一次。
  • 测到的水温已经达到设定温度:停止加热,提示完成。
电热水壶烧水的流程图:未达到温度时箭头回到加热
电热水壶烧水的流程图。菱形表示判断;「否」那一支回到前面的测温,同一段步骤可以发生多次,直到温度达到。

仔细观察这张流程图,其中有一条箭头从下面回到上面,使得一些动作可以重复发生多次,直到满足特定的条件,才会有另一支箭头把程序带出重复。这就是一个典型具有循环结构的程序。

循环的思想激发了工业时代的繁荣,工厂将产品的生产切分成大量标准化的动作,每个岗位的工人只需要重复地执行相同的动作,使得产品的生产被加速、被计量、被交给装置。

福特高地公园工厂装配线,1913年
福特高地公园工厂装配线,1913年。来源:美国国家档案馆 / Wikimedia Commons,公有领域。

把这一逻辑从工厂搬进计算机:计算机中的代码也像是岗位工人,循环结构使其中的一些代码重复地执行,直到满足条件。

只要...就还...

#explore-while

在 Python 里,我们可以使用 while 来描述一个简单的循环结构,意味着在条件成立时重复执行下方的代码。

python
n = 5
while n > 0:
    print(n)
    n = n - 1

这段代码与 if 的选择结构非常相似,我们可以尝试将其中的 while 改成 if,对比一下输出的不同:

python
n = 5
if n > 0:
    print(n)
    n = n - 1

可以看到使用 while 时,程序一共输出了5次,说明子结构中的代码被执行了5次,确实发生了循环;在使用 if 时,只输出了1次,说明子结构中的代码仅被执行了1次,没有发生循环。

让我们再来看看为啥循环了5次。

在子结构代码中的变量 n,在每一次执行时值都会减一。因为初始值是 5 ,因此在循环到第5次时,n 的值变成了 0。当程序跳转回 while 处时,就不再满足判断条件,于是结束了循环。

请注意!如果没有在子结构中修改变量值,判断条件永远为真,循环将永远无法结束,程序进入到死循环。

python
# 这段代码为死循环,可能导致IDE崩溃,请慎重执行
n = 5
while n > 0:
    print(n)

依次遍历...

#explore-for

还有一种常见的循环情况是,我们要对若干个值进行相似的处理,我们就可以使用 for...in... 来对这些值进行遍历:

python
# 遍历0,1,2,3四个值,分别计算和显示它们的平方数
for i in 0,1,2,3:
    print(i**2)

在上面这段代码中,子结构将会被运行4轮。每一轮子结构运行的最开始时,变量 i 的值将依次被赋值为 0、1、2、3。

当然我们也可以使用for...in...来遍历若干个变量。

python
a = 2
b = 4
c = 6
d = 8
for i in a,b,c,d:
    print(i//2)

子结构中的i的值将被依次赋值为a、b、c、d变量的当前值,在完成计算后输出。

关于for...in...对于变量的遍历,我们将会在第6课的复杂容器中再次讨论。

如果我们需要的是类似上面这样、一组长且连续的数字,也可以不必手打,借助range()区间生成器来辅助生成。

python
# 使用range来生成0,1,2,3,4五个值
for i in range(0,5):
    print(i**2)

range(a, b)指令中的两个数字a``b表示区间的开始和结束的位置,为前闭后开区间,即包含a不包含b。

如果是从0开始计数,在不引发歧义的情况下,a也可以省略不写。

python
# range起点为0时,可以省略
for i in range(5):
    print(i**2)

range(a,b,c)还支持使用第三个数来表示增幅(步长),也就是说间隔不一定为1,可以是任意大小。

python
# 生成从2到10的连续数字,每次间隔为2,结果为2、4、6、8
for i in range(2, 10, 2):
    print(i**2)

想想看,借助步长可以是任意值的特性,有没有可能实现倒数循环?

不论是while还是for...in...都支持循环嵌套,也就是在循环中还有循环,可以按需求随意混用。

python
# 从4倒数到1,每个数字是几就打印几次
a = 4
while a > 0:
    for i in range(a):
        print(a)
    a -= 1

在使用两层for...in...循环时,要避免使用相同名字的循环变量,以避免混淆。

python
# 求1、2、3三个数互相相乘的积
for i in range(1,4):
   for j in range(1,4):
       print(i*j)

循环也可以和 if 互相嵌套使用。

python
# 找出100以内所有能被7整除的数字
for i in range(100):
    if i % 7 == 0:
        print(i)

循环的提前结束

#explore-break

有些时候,我们不必等循环完全结束,比如在区间10~20中找到第一个能被3整除的数,在找到12时就已经达成目标了。在Python中,可以使用 break 来提前结束循环。

python
# 只有第一个能被3整除的12被输出了,15、18并不会输出
for n in range(10, 20):
    if n % 3 == 0:
        print(n)
        break 

在上面这段代码中,只有第一个能被3整除的12被输出了,15、18并不会输出,因为在输出12后,循环就被break提前终止了。

还有一个非常相似的情况,并不是提前结束整个循环,而是提前结束循环中的某一次。比如在区间10~20中跳过所有不能被3整除的数。在Python中,可以使用 continue 来处理这种情况。

python
# 所有不能被3整除的数字都会被跳过
for n in range(10, 20):
    if n % 3 != 0:
        continue 
    print(n)

本节练习:加到越过 50

#practice

在 在线 IDE 里令 n = 1。只要 n 小于 50,就先打印 n,再把 n 加上 7。应当依次打印 1、8、15、22、29、36、43。下一次会变成 50,条件不再成立,所以 50 不打印。

本节要点

#review
  • 流程图里有向回的箭头时,同一段可以重复,直到条件把流程带出——这就是循环结构。
  • while 在条件成立时反复执行子结构;必须在子结构里改变条件所依赖的量,否则会进入死循环。与 if 外表相似,但 if 只走一次。
  • for...in... 依次遍历给出的值;range(a, b) 是前闭后开,可省略起点 0,也可用第三个数设步长。
  • break 提前结束整个循环;continue 跳过本轮剩余语句,进入下一轮。

单选题

while 会重复

运行后输出是?

n = 5
while n > 0:
    print(n)
    n = n - 1

单选题

死循环的原因

这段程序很可能怎样?

n = 5
while n > 0:
    print(n)

单选题

range 的区间

运行后输出是?

for i in range(2, 10, 2):
    print(i)

单选题

break 的作用

运行后输出是?

for n in range(10, 20):
    if n % 3 == 0:
        print(n)
        break

单选题

continue 的作用

运行后输出是?

for n in range(10, 20):
    if n % 3 != 0:
        continue
    print(n)

未登录时不会保存学习进度