去做题

第九单元:循环步长 · 让循环大步流星

Unit 9: Loop Step — Iterating with a Stride
计算机科学 5 · 安息云峰 Cloudrip Mountain · 第 14–15 关:边缘防御 → 危险的路线

9.1 什么是循环步长

What Is a Loop Step

山林中有一座宁静的村庄,静得反常——食人魔就要来了!你需要在村庄四周布置哨塔和火焰陷阱。这些防御点不是随意摆放的,而是沿着边界、每隔固定距离放一个。如果用普通循环一个坐标一个坐标地写,代码会又长又乱。本单元你将学会 for 循环的第三个参数——步长(step),让循环变量不再只是"加一加一"地走,而是能大步流星地跳跃前进,甚至倒着走。

本单元主线:range(start, end, step) 的第三个参数 step 决定循环变量"每次跳多远"。正的 step 让它递增,负的 step 让它递减。配合 hero.moveXY(x, y) 沿边巡逻、hero.buildXY(type, x, y) 定点建造,你只用几行循环就能在村庄四条边上等距布满防御工事,把大量重复代码压缩成简洁的几行。

9.1.1 从"逐一"到"跳跃"(From One-by-One to Striding)

9.1.1.1 默认步长是 1 The default step is 1

之前你写的 range(0, 5) 会依次产生 0、1、2、3、4——每次悄悄地加 1,这个"1"就是默认步长。但很多时候我们不想一格一格挪:布防的点每隔 20 个单位一个,倒计时每 10 秒报一次,跳格子每次跳 2 步……这时就要显式地把步长写出来。关卡概览一语道破:"for 循环不仅可以用于遍历数组,还可以一次增加 2、4、10 甚至 20 个单位!"

# 默认步长 1
for i in range(0, 5):
    print(i)      # 0 1 2 3 4

# 步长 10,一次跳 10
for i in range(0, 100, 10):
    hero.say(i)   # 0 10 20 ... 90

9.1.2 步长解决什么问题(Why Step Matters)

9.1.2.1 等距布点,天生适合步长 Evenly-spaced points love a step

凡是"从某处开始、每隔固定距离做一次、到某处结束"的任务,步长循环都是最优解:沿墙每 20 米放一个哨塔、每 24 米埋一个陷阱、每 5 单位走一步。你只需告诉循环三件事——从哪开始、到哪结束、每次跨多大——剩下的重复动作它替你完成。

扣哒世界 步长布点示意 - koudashijie.com
图1:沿边界等距布防——从起点出发,每隔固定步长放一个防御点,直到终点前停下

9.2 range 三参数

The Three-Argument range()

9.2.1 start / end / step(三个位置参数)

9.2.1.1 起点、终点、步长 Start, end, and step

range(start, end, step) 有三个参数,位置和含义都固定:start 是起点(包含在内),end 是终点(不包含),step 是每次前进的量。循环变量从 start 出发,每轮加上 step,一旦越过 end 就停下。

range 的三个零件 range ( 40 , 81 , 20 ) start 起点 end 终点(不含) step 步长

9.2.2 end 取不到(左闭右开)(The End Is Exclusive)

9.2.2.1 为什么写 81 而不是 80 Why we write 81, not 80

这是最容易踩的坑。range 遵循"左闭右开":起点算数,终点不算。关卡北边界要从 x=40 走到 x=80(含 80),步长 20,如果写 range(40, 80, 20),产生的是 40、60——80 被漏掉了!所以必须把 end 写成比目标终点稍大的数,如 range(40, 81, 20),才能产生 40、60、80。默认代码的注释也特意提醒:"range 不包括第二个边界。"

写法产生的序列含 80 吗
range(40, 80, 20)40, 60❌ 漏了
range(40, 81, 20)40, 60, 80✅ 正确
口诀:想让循环"走到并包含"某个终点,就把 end 写成"那个终点 + 1"(正步长)或"那个终点 − 1"(负步长)。只要 end 落在"最后一个想要的值"和"下一个会跳到的值"之间即可,写 +1 最稳妥。

9.3 正步长与负步长

Positive & Negative Steps

9.3.1 正步长递增(Counting Up)

9.3.1.1 从小往大,step 为正 Ascending: step is positive

当 step 为正数时,循环变量从 start 一路递增到接近 end。此时必须满足 start < end,否则循环一次都不执行。关卡里"沿北边界从 x=40 到 x=80"就是典型的正步长。

# 正步长:20, 40, 60, 80
for i in range(20, 100, 20):
    hero.say(i)   # 20 40 60 80

9.3.2 负步长递减(Counting Down)

9.3.2.1 从大往小,step 为负 Descending: step is negative

当 step 为负数时,循环变量从 start 一路递减到接近 end。此时必须满足 start > end(起点比终点大)。关卡里"沿东边界从 y=40 到 y=20"就要用负步长:range(40, 19, -20) 产生 40、20——注意 end 写成 19(比 20 小 1),才能把 20 包含进来。

# 负步长:40, 20(end 写 19 才含 20)
for y in range(40, 19, -20):
    hero.moveXY(80, y)
正步长向右递增,负步长向左递减 step=+20 40 60 80 step=−20 40 20
方向和步长必须一致:正步长要求 start < end,负步长要求 start > end。如果方向反了——比如 range(40, 20, 20)(起点大于终点却用正步长),或 range(20, 40, -20)(起点小于终点却用负步长)——range 会什么都不产生,循环体一次也不执行,防御点全没建成。

9.4 按间隔移动坐标

Moving Coordinates by Intervals

9.4.1 moveXY 沿边巡逻(Patrolling with moveXY)

9.4.1.1 一条边固定一个坐标,另一个坐标随循环变化 Fix one coordinate, vary the other

沿一条边巡逻的诀窍是:那条边上有一个坐标是固定的,另一个坐标随循环变量变化。走北边界(y=60 不变)时,让 x 随循环从 40 跳到 80;走东边界(x=80 不变)时,让 y 随循环从 40 降到 20。每到一个点就 hero.moveXY(x, y) 移动过去。

# 北边界:y 固定 60,x 从 40 递增到 80
for x in range(40, 81, 20):
    hero.moveXY(x, 60)
    hero.say("Here")

9.4.2 buildXY 定点建造(Building with buildXY)

9.4.2.1 buildXY(类型, x, y) 直接在坐标造东西 buildXY places a structure at (x, y)

hero.buildXY(type, x, y) 让英雄直接在指定坐标建造指定类型的建筑,无需先移动过去。它比"先 moveXY 再建"更省时——在"危险的路线"里,你只有 80 秒,用 buildXY 能一口气把火焰陷阱布满四条通道。第一个参数是建筑类型字符串(如 "fire-trap"),后两个是坐标。

moveXY(先走过去)
hero.moveXY(x, 60)
# 英雄本体移动到该点
buildXY(远程建造)
hero.buildXY("fire-trap", x, 114)
# 直接在该点造陷阱

9.5 用循环消灭重复

Killing Repetition with Loops

9.5.1 循环 vs 手写重复(Loop vs. Copy-Paste)

9.5.1.1 一行循环顶十几行手写 One loop replaces a dozen lines

如果不用步长循环,你得手写每一个坐标:buildXY("fire-trap", 40, 114)、buildXY("fire-trap", 64, 114)、buildXY("fire-trap", 88, 114)……不仅冗长,改起来还容易出错。用一行 for x in range(40, 113, 24): 就能全部搞定。这正是编程里最重要的原则之一——DRY(Don't Repeat Yourself,不要重复自己)。

❌ 手写重复(啰嗦易错)
hero.buildXY("fire-trap",40,114)
hero.buildXY("fire-trap",64,114)
hero.buildXY("fire-trap",88,114)
hero.buildXY("fire-trap",112,114)
✅ 步长循环(简洁清晰)
for x in range(40, 113, 24):
    hero.buildXY("fire-trap", x, 114)



# 一行循环 = 四行手写
再进一步:用函数分组:"危险的路线"把四条边各自封装成 buildNorthLine()、buildEastLine()、buildSouthLine()、buildWestLine() 四个函数,主程序只需依次调用它们。这是"步长循环"和第一单元"函数封装"的组合拳——循环消灭一条边内部的重复,函数消灭四条边之间的结构重复。

9.6 实战关卡精讲

Level Strategy & Analysis

这一节聚焦每一关的策略、思路与算法设计,只展示用于说明的关键片段,完整通关程序请你自己动手完成。

第 14 关 · 边缘防御

🛡️ 边缘防御 —— 沿四条边界巡逻布塔

扣哒世界 边缘防御关卡 - koudashijie.com
图2:边缘防御——沿村庄四条边界等距巡逻,在每个点让农民建造哨塔

关卡背景与目标

宁静的村庄需要坚固的防御工事。每个农民都能造一座自动哨塔,塔会攻击城外一切单位。你要做的是沿着村庄的四条边界巡逻,每走到一个点就 hero.say("Here") 告诉农民在此建造,绕村一圈后躲回村里。

策略:把正方形拆成四条边

这是一个正方形边界,四条边各写一个 for 循环。关键是想清楚每条边"哪个坐标固定、哪个坐标变、往哪个方向走、用正还是负步长"。绕村一圈是顺时针:北边向右(x 增)、东边向下(y 减)、南边向左(x 减)、西边向上(y 增)。默认代码已经替你写好北、东两条边,留下南、西两条边让你补全。

思路拆解

四条边的方向和步长符号一一对应。北边界 y=60,x 从 40 递增到 80,用 range(40, 81, 20)(正步长)。东边界 x=80,y 从 40 递减到 20,用 range(40, 19, -20)(负步长,end 写 19 才含 20)。照此推理:南边界 y=20,x 从 60 递减到 20,用 range(60, 19, -20);西边界 x=20,y 从 40 递增到 60,用 range(40, 61, 20)。每个点都 hero.moveXY(...) 后 hero.say("Here")。

# 关键片段:需要你补全的南、西两条边
# 南边界 y=20,x 从 60 递减到 20(负步长)
for x in range(60, 19, -20):
    hero.moveXY(x, 20)
    hero.say("Here")

# 西边界 x=20,y 从 40 递增到 60(正步长)
for y in range(40, 61, 20):
    hero.moveXY(20, y)
    hero.say("Here")
四条边的方向记忆法:顺时针绕村一圈——北边"→"(x 增,正步长)、东边"↓"(y 减,负步长)、南边"←"(x 减,负步长)、西边"↑"(y 增,正步长)。坐标变大用正步长,变小用负步长,方向别搞反。
最后别忘躲回村里:绕完一圈,代码末尾要 hero.moveXY(50, 40) 让英雄退到村庄中心的安全位置,否则英雄留在边界上可能被敌人攻击。
第 15 关 · 危险的路线

🔥 危险的路线 —— 四条通道埋满火焰陷阱

扣哒世界 危险的路线关卡 - koudashijie.com
图3:危险的路线——在四个方向的所有通道上等距埋下火焰陷阱,抢在食人魔猎人队到达前完成

关卡背景与目标

通往村庄的入口很多,你必须在四个方向的所有通道都埋下火焰陷阱。食人魔猎人队 80 秒后就到——时间紧迫,正是用 for 循环节省时间、让代码简洁的最佳场景。这一关引入了 buildXY,可以远程建造,比逐点移动快得多。

策略:四条边各写一个建造函数

本关的结构比"边缘防御"更进一步:它把四条边分别封装成 buildNorthLine、buildEastLine、buildSouthLine、buildWestLine 四个函数,主程序末尾依次调用四个函数、最后 hero.moveXY(40, 94) 撤离。默认代码给好了北线函数作示范,其余三个函数体是 pass,等你填。策略就是:读懂每条边的注释(起止坐标 + 步长),把对应的 for 循环 + buildXY 填进各自的函数里。

思路拆解

每条边的参数都写在注释里,照着翻译即可。北线 y=114,x 从 40 到 112 步长 24:range(40, 113, 24)。东线 x=140,y 从 110 到 38 步长 18(递减):range(110, 37, -18)。南线 y=22,x 从 132 到 32 步长 20(递减):range(132, 31, -20)。西线 x=20,y 从 28 到 108 步长 16(递增):range(28, 109, 16)。递增用正步长、递减用负步长,end 记得比目标终点多走/少走一点以便包含终点。

# 关键片段:用函数封装每条边(示例两个)
def buildEastLine():
    # x=140 固定,y 从 110 递减到 38,步长 -18
    for y in range(110, 37, -18):
        hero.buildXY("fire-trap", 140, y)

def buildWestLine():
    # x=20 固定,y 从 28 递增到 108,步长 16
    for y in range(28, 109, 16):
        hero.buildXY("fire-trap", 20, y)

# 定义后务必调用四个函数
buildNorthLine()
buildEastLine()
buildSouthLine()
buildWestLine()
定义 ≠ 调用:把三个 pass 换成循环只是"写好了函数体"。别忘了主程序末尾的 buildNorthLine()…buildWestLine() 四行调用——没有它们,函数里的代码一个陷阱都不会建。这一点和第一单元"定义后必须调用"是同一个道理。
为什么用 buildXY 而不是先 moveXY?这关只有 80 秒,英雄若逐点跑过去建造会浪费大量时间在路上。buildXY 让英雄站在原地就能远程放置陷阱,四条边几十个点几乎瞬间布完——在限时关卡里,选对 API 就是选对了策略。
别让步长把点漏在边界外:比如东线注释说"到 y=38",但 range(110, 37, -18) 实际产生 110、92、74、56、38——正好压线到 38。动手前不妨在纸上把序列展开算一遍,确认既没漏点、也没有点落到通道之外。

9.7 常见错误

Common Mistakes

9.7.1 新手最容易踩的坑(Pitfalls for Beginners)

9.7.1.1 步长循环的典型问题 Typical issues with stepped loops

扣哒世界 循环步长常见错误速查 - koudashijie.com
图4:终点漏一格、步长符号搞反、函数定义了不调用,是本单元最常见的失误
易错点清单:
  • 终点漏一格——想包含 80 却写 range(40, 80, 20),结果漏掉 80。正步长把 end 写成"终点 +1"。
  • 负步长 end 没减够——想包含 20 却写 range(40, 20, -20),20 被排除。负步长把 end 写成"终点 −1"。
  • 步长符号搞反——坐标要变小却用了正步长(或反之),range 什么都不产生,循环体一次不执行。
  • 方向与起止矛盾——正步长却 start > end、负步长却 start < end,同样得到空循环。
  • 固定坐标写错——沿北边界时 y 应恒为 60,若误把 y 也写成变量,点就跑偏了。
  • 函数定义了不调用——把 pass 换成循环后忘了在主程序调用该函数,陷阱不会建成。
  • buildXY 参数顺序错——应是 buildXY("类型", x, y),类型字符串在前,别和坐标顺序弄混。
  • 忘记撤离——绕完/建完后没让英雄退到安全点,英雄暴露在边界被敌人攻击。

本单元小结 ✨

恭喜你守住了山林中的村庄!这一单元你解锁了 for 循环的第三个参数——步长,让循环从"逐一挪动"升级为"大步跳跃、还能倒着走"。你掌握了 range(start, end, step) 的三个参数含义、牢记了"左闭右开、end 取不到"的规则,学会了用正步长递增和负步长递减沿四个方向遍历坐标,用 moveXY 巡逻、用 buildXY 定点建造,并把"步长循环"和"函数封装"组合起来,用极简的代码消灭了大量重复。你在"边缘防御"里绕村布塔、在"危险的路线"里限时埋雷,两关都漂亮通过。来回顾一下核心概念:

核心概念关键点对应关卡
range 三参数range(start, end, step)两关
左闭右开start 含、end 不含两关
正步长递增step>0 且 start<end两关
负步长递减step<0 且 start>end两关
end 补偿含终点:+1 或 −1两关
moveXY 巡逻固定一坐标、变另一坐标边缘防御
buildXY 建造buildXY("类型", x, y)危险的路线
循环消灭重复DRY,一行顶多行两关
函数封装每条边定义后必须调用危险的路线

懂得用一个小小的步长参数掌控循环节奏、用循环与函数把重复代码化繁为简的英雄,
才是云峰之上真正高效的程序员。继续向雪山之巅挺进吧,勇敢的程序员!🏔️🛡️🔥

扣哒世界 编程知识 · 计算机科学 5(安息云峰):第九单元

扣哒世界教研团队