CC++ & Algorithm

Python 循环控制与综合应用

困难3
语言版本:C++Python
概述:综合运用break、continue、else子句以及循环的嵌套,解决更复杂的问题。

循环控制进阶:让程序变得更聪明、更稳健

在编程中,循环帮我们重复做事情,但有时候我们需要在循环中途跳出来、跳过某次循环、或者只在循环正常结束时才执行某段代码。Python 提供了几个好用的工具:breakcontinue、以及循环的 else 子句。另外,把循环嵌套起来(放在另一个循环里面),再配合这些控制工具,就能解决很多实际生活中遇到的有趣问题。比如:老师点名(循环每一个学生),找零钱组合(循环所有硬币面值),猜数字游戏(循环猜拳直到猜对或次数用尽)。下面我们一步步来学习。


一、循环的 else 子句:只有完整跑完才“吹哨”

1. 它干什么用的?

forwhile 循环后面都可以跟一个 else 块。这个 else 块只在 循环正常结束(没有因为 break 而提前退出)时才会执行。如果循环被 break 打断了,else 块就不执行。

想象一下:你每天放学后要在操场上跑 5 圈(循环 5 次)。如果中途下雨了,你躲进教室(相当于 break),那么跑圈就没有完成,老师不会给你奖励(else 不执行)。如果你坚持跑完了 5 圈(自然结束),老师就奖励你一颗糖(else 执行)。

2. 生活中的例子:检查全班同学有没有人忘带作业

你负责检查 10 个同学的作业。你从第 1 个问到第 10 个,如果遇到一个没带作业的,你就喊“有人没带作业!”然后停止检查(break)。如果全部检查完都没有人忘带,你就说“全班都带了!”(else 执行)。

代码实现:

students = ["小明", "小红", "小刚", "小丽", "小华"]
has_no_homework = True  # 假设每个同学都带了?实际上我们用循环检查

# 模拟检查过程:只有小丽没带作业
homework_status = {
    "小明": True,
    "小红": True,
    "小刚": True,
    "小丽": False,
    "小华": True
}

for student in students:
    if not homework_status[student]:  # 如果这个学生没带作业
        print(f"{student} 没带作业!")
        break
else:
    print("全班都带了作业!")

输出结果:

小丽 没带作业!

(没有执行 else 部分,因为被 break 打断了)

如果把小丽也改成带了作业,那么循环自然结束,就会打印“全班都带了作业!”。

3. 判断质数的经典例子(现有内容扩充)

判断一个数是不是质数,可以用 else 配合 for 循环。如果循环内部没有发现任何能整除的数(break 没执行),那么 else 就会执行,说明这个数是质数。

# 判断 2 到 9 哪些是质数
for i in range(2, 10):            # i 从 2 到 9
    for j in range(2, i):         # j 从 2 到 i-1
        if i % j == 0:            # 如果 i 能被 j 整除
            print(f"{i} 不是质数")
            break                  # 跳出内层循环
    else:
        print(f"{i} 是质数")

输出:

2 是质数
3 是质数
4 不是质数
5 是质数
6 不是质数
7 是质数
8 不是质数
9 不是质数

注意:else 是跟内层 for 对齐的,不是跟外层 for。它表示:内层循环如果完整跑完(没有 break),就说明 i 没有因数,是质数。


二、用标志变量优雅地跳出多重循环

有时候我们会遇到嵌套循环(循环里套循环),比如想要找一对数字 (x, y) 满足某个条件。当找到后,我们希望同时跳出内外两层循环。直接 break 只能跳出内层循环,外层还需要再判断一次才能跳出。解决办法有两种:

方法1:使用标志变量(flag)

设置一个变量 found,初始为 False,找到目标后就设为 True,然后在内层 break 后,在外层检查这个变量,再 break

生活中的例子:你在抽屉里找钥匙,抽屉里又分好几个小格子。你一层一层翻,如果找到了,你就不需要再翻剩下的格子和抽屉了。这里的“找到”就是标志。

# 寻找两个正整数(都在 1-9 之间),它们的乘积等于 36
found = False  # 标志变量,初始为 False(还没找到)
for x in range(1, 10):
    for y in range(1, 10):
        if x * y == 36:
            print(f"找到组合:{x} x {y} = 36")
            found = True  # 标记已找到
            break          # 跳出内层循环
    if found:              # 如果已经找到了,就再跳出外层循环
        break

方法2:把循环放在函数里,用 return

当我们把嵌套循环放入一个函数,只要找到目标就可以用 return 直接退出函数,比标志变量更简洁。

def find_product(target):
    """在 1-9 范围内找两个数乘积等于 target,打印并返回第一个找到的组合"""
    for x in range(1, 10):
        for y in range(1, 10):
            if x * y == target:
                print(f"找到组合:{x} x {y} = {target}")
                return x, y  # 直接退出函数

find_product(36)

常见错误:新手容易忘记在外层也写一个 break,导致虽然内层跳出了,但外层还在继续循环,结果打印出多个组合。使用函数 return 可以避免这个麻烦。


三、综合案例:猜数字增强版(包含异常处理和 continue)

我们已经有一个猜数字游戏:程序随机生成一个 1~100 的数字,玩家有 5 次机会。如果猜对了就用 break 结束;如果 5 次都没猜对,循环正常结束,else 块提示机会用完。

现在我们要让程序更健壮:如果用户输入的不是数字(比如输入了“abc”),程序要提示“请输入数字”,并且 这次不算次数(让用户重新输入)。这就要用到 try-except 捕获异常(处理输入错误)和 continue 跳过本次循环。

完整代码(带中文注释)

import random

# 生成一个 1 到 100 之间的随机整数
secret = random.randint(1, 100)
max_chances = 5           # 最多可以猜 5 次
chances_used = 0          # 已经用了多少次(可选统计)

print("我想到一个1到100之间的数字,你有", max_chances, "次机会。")

while chances_used < max_chances:
    user_input = input(f"第 {chances_used+1} 次猜:")
    try:
        guess = int(user_input)   # 尝试将输入转为整数
    except ValueError:            # 如果转换失败(输入不是数字)
        print("请输入数字!这次不消耗次数。")
        continue                  # 跳过本次循环,不增加次数

    # 只有输入合法数字才消耗次数
    chances_used += 1

    if guess == secret:
        print("恭喜你,猜对了!")
        break                     # 猜对就结束
    elif guess > secret:
        print("大了")
    else:
        print("小了")
else:
    # 如果 while 循环因为条件不满足(次数用尽)正常结束,
    # 而且没有被 break 打断,就会执行 else
    print(f"机会用完了,数字是 {secret}。")

注意:这里把 for 改成了 while,因为 continue 后不希望增加 chances_used,用 for 会不好控制。实际上用 while 配合手动计数更灵活。elsewhile 循环中同样有效。


四、常见错误与提醒

  1. 忘记 else 是和 for 还是 if 对齐?
    else 是和最内层的 forwhile 对齐的,不要习惯性地认为和 if 配对。例如判断质数时,else 是跟内层 for 的,不是跟 if 的。

  2. break 只能跳出当前一层循环。如果想跳出多层,必须用标志变量或函数 return。新手经常只在内层 break,以为外层也会停下,结果得到多余输出。

  3. continue 会跳过本次循环的剩余代码,但不会影响循环的迭代次数。在 for 循环中,它会直接进入下一次迭代;在 while 循环中,它回到条件判断。如果 while 里没正确更新计数器,可能导致死循环。比如上面猜数字例子中,如果用 for 并且 continue 后依然 attempt 会自增,就不能实现“不消耗次数”的效果,所以我们改用了 while

  4. else 在循环中的作用和 if-else 完全不同,初学者容易混淆。只有在循环没有被 break 打断时,else 才会执行。


五、完整示例:模拟自动售货机找零

我们来一个贴近生活的综合例子:自动售货机里有 1元、5元、10元三种硬币,需要给顾客找零。输入要找的金额(整数,比如 18 元),程序尝试用尽量少的硬币(贪心算法)输出组合。这里我们用嵌套循环和 break 控制。

# 自动售货机找零:用最少硬币组合(1元 5元 10元)
def change_money(target):
    """找到 1元 5元 10元 的组合,使硬币数量最少"""
    found = False                   # 标志变量,是否找到解
    # 先尝试多用大面值硬币(贪心思想)
    for ten in range(target // 10, -1, -1):      # 10元硬币最多 target/10 个,从多到少
        for five in range(target // 5, -1, -1):  # 5元硬币最多 target/5 个
            one = target - (ten * 10 + five * 5)
            if one >= 0:                         # 剩余金额用 1元补足
                print(f"找零 {target} 元:10元 x {ten},5元 x {five},1元 x {one}")
                found = True
                break                           # 找到解就跳出内层循环
        if found:
            break                               # 跳出外层循环

change_money(18)   # 调用函数

输出:

找零 18 元:10元 x 1,5元 x 1,1元 x 3

这个程序利用了嵌套循环和标志变量,从大面值开始尝试,找到第一个解(硬币数最少)后立即跳出所有循环。


六、相关知识点指引

  • 要想在输入出错时重试,除了 try-except,还可以用 while True 循环加 break 实现“直到输入合法才继续”。
  • 如果你想深入学习异常处理,可以看《Python 异常捕获与调试》。
  • 关于循环嵌套的更多技巧,可以学习《二维列表与矩阵操作》。
  • 如果你想用循环控制玩更复杂的游戏,比如“猜数字”加上难度选择、记录历史猜测,可以看《函数与模块化设计》。

掌握这些循环控制工具后,你就能写出更智能、更稳健的程序了——比如自动检查班级作业、猜数字游戏不怕乱按、自动售货机聪明找零。快去试试吧!

例题精讲

1单选题

以下Python代码的输出是什么? for i in range(3): for j in range(3): if j == 1: break print(i, j)

A0 0\n1 0\n2 0
B0 0\n0 1\n1 0\n1 1\n2 0\n2 1
C0 0\n0 1\n0 2\n1 0\n1 1\n1 2\n2 0\n2 1\n2 2
D0 1\n1 1\n2 1
2判断题

在Python中,如果for循环正常结束(没有遇到break),则else子句中的代码会被执行。

3填空题
下面的代码使用嵌套循环和continue语句,统计0到9中所有不包含数字3的两位数的个数(十位从1-9,个位0-9)。请补全代码。
count = 0
for i in range(1, 10):
    if i == 3:
        ___
    for j in range(10):
        if j == 3:
            ___
        count += 1
print(count)
4单选题

以下代码的输出结果是什么? x = 0 while x < 5: x += 1 if x == 3: continue print(x, end=' ') else: print("完成")

A1 2 4 5 完成
B1 2 3 4 5 完成
C1 2 4 5
D1 2 3 4 5
5填空题
下面的代码使用for-else结构,在列表numbers中查找第一个大于10的数字,找到后打印该数字并退出循环;如果没找到,则打印"未找到"。请补全代码。
numbers = [3, 7, 9, 12, 5]
target = None
for num in numbers:
    if num > 10:
        target = num
        ___
else:
    print("未找到")
if target is not None:
    print("找到:", target)