欢迎来到计算机科学 5!在前面的旅程里,你已经掌握了变量、条件、循环、数组、字典,还学会了召唤和指挥军队。但随着程序越写越长,你会发现同一段逻辑要反复出现,主循环挤得密密麻麻、越来越难读。本单元你将拿到一件真正把编程能力"提高到下一个等级"的工具——函数(function)。关卡"关键力量"的开场白说得很直白:"用新的概念:函数,把你的编程能力提高到下一个等级!"
def 定义函数、用缩进写函数体、理解函数内代码"先保存、后执行"的特性,并在 while 循环里调用自己写的函数,把"捡金币、召唤兵、指挥兵"这些重复行为各自封装成一个函数。
关卡概览一语中的:"函数非常适合将高级行为分解为更多小的部分。"想象你的主循环里要做三件事——捡最近的金币、召唤士兵、指挥士兵攻击。如果全堆在一起,循环体又长又乱。函数的思路是:把"捡金币"那几行打包成 pickUpNearestCoin,把"召唤兵"打包成 summonSoldier,把"指挥兵"打包成 commandSoldiers。主循环就变成清清爽爽的三行调用。
使用函数永远分两步:先定义(告诉程序"这个名字对应哪些代码"),再调用(真正让这些代码跑起来)。这两步是分开的——只定义不调用,代码一行都不会执行。关卡特别强调:"请记住,在定义函数之后,你必须调用它!"这是初学者最常忘的一点。
定义函数用关键字 def(definition 的缩写)开头,后面跟函数名、一对括号 ()、再加一个冒号 :。函数名要能说明它"做什么",通常用动词短语,如 pickUpNearestCoin(捡起最近的金币)、summonSoldier(召唤士兵)。这一行叫函数的"头部"。
头部那行以冒号结尾,紧接着的缩进代码块就是"函数体"——真正要做的事。Python 靠缩进(通常 4 个空格)来判断哪些行属于这个函数:缩进在里面的就属于它,一旦回到不缩进,就说明函数结束了。下面这个 pickUpNearestCoin 是关卡给好的示范。
# 定义:让英雄收集最近的金币 def pickUpNearestCoin(): items = hero.findItems() # ← 缩进,属于函数体 nearestCoin = hero.findNearest(items) if nearestCoin: hero.move(nearestCoin.pos) # ← 更深缩进,属于 if
这是函数最关键、也最容易被忽略的特性。关卡默认代码的注释写得非常清楚:"放在函数内的代码并不会立刻执行,而是先保存好,以备后用。"也就是说,当程序读到 def pickUpNearestCoin(): 这一段时,里面的 findItems、move 统统不会马上发生;它们只是被记下来、贴上 pickUpNearestCoin 这个标签,静静等待你日后"点名"。
要让保存好的函数真正执行,就调用它——写下函数名,末尾加一对括号 ()。关卡说得明明白白:"要调用一个函数,请在函数名称末尾使用 (),如 commandSoldiers()。"注意区别:定义时写 def 名字():,调用时只写 名字()——没有 def、不缩进在别的函数里、末尾也没有冒号。
def summonSoldier(): # ...函数体... pass
summonSoldier() # 无 def、无冒号、不缩进
summonSoldier 而漏掉括号,Python 不会执行它,只是"提到"了这个函数。加上 (),才是真正下令"现在就跑一遍"。
把函数调用放进 while True,主循环就变成一份清爽的"行动清单":每一帧依次点名三个函数,游戏就会反复执行"捡币→召唤→指挥"。关卡默认代码提示:"在你的循环里,你可以'调用'你在上面定义的函数……下面一行代码会让 pickUpNearestCoin 函数里的代码被执行。"
while True: pickUpNearestCoin() # 捡最近的金币 summonSoldier() # 有钱就召唤 commandSoldiers() # 指挥全军攻击
关卡给出了一条重要的设计原则:"每一项行动都要有自己的函数!"也就是一个函数只专注做一件事:pickUpNearestCoin 只管捡币、summonSoldier 只管召唤、commandSoldiers 只管指挥。这样每个函数都短小、清晰、好测试,哪块出了问题也容易定位。这是编程里"关注点分离"的入门体现。
找到所有物品里最近的一个金币,移动过去捡起来。只负责"捡钱"。
判断 gold 是否够 costOf("soldier"),够就召唤。只负责"扩军"。
遍历所有士兵,各自找最近敌人并下令攻击。只负责"作战"。
这一节聚焦每一关的策略、思路与算法设计,只展示用于说明的关键片段,完整通关程序请你自己动手完成。
本关是函数的入门关。关卡已经替你写好了 pickUpNearestCoin 和 commandSoldiers 两个函数,留下 summonSoldier 让你补全,并要求你在主循环里把三个函数都调用一遍。策略非常清晰:让程序每帧都"捡币攒钱→有钱扩军→指挥作战",同时英雄注意躲避食人魔的攻击。
算法思路是两件事。其一,补全 summonSoldier 函数体:在函数里写一个 if 判断——if hero.gold >= hero.costOf("soldier"): 就 hero.summon("soldier"),把"钱够才召唤"的逻辑封装进去。其二,在 while 循环里调用三个函数:默认代码已经调用了 pickUpNearestCoin(),你需要在下面补上 summonSoldier() 和 commandSoldiers()——因为"定义之后必须调用,否则函数里的代码永远不会跑"。
# 关键片段一:补全 summonSoldier 的函数体 def summonSoldier(): if hero.gold >= hero.costOf("soldier"): hero.summon("soldier") # 关键片段二:在主循环里逐一调用三个函数 while True: pickUpNearestCoin() summonSoldier() commandSoldiers()
summonSoldier 只是写好了菜谱,必须在 while 里写上 summonSoldier() 才会真正召唤士兵"。很多人卡在"函数写对了却不生效",十有八九是忘了调用。
本关的设定很有意思:双生花和花匠命运相连——花匠被攻击,双生花就缩小;缩到普通花朵大小后花匠会开始受伤;花匠平安时双生花则会长大。所以策略核心是尽力保护花匠:不停召唤士兵、指挥士兵清剿逼近的敌人、捡金币维持扩军,用一支持续壮大的部队把攻击挡在花匠之外,撑到胜利。
与"关键力量"不同,本关的 summonSoldiers 已给好,需要你亲手定义 commandSoldiers 和 pickUpNearestCoin 两个函数,并在 while 循环里给它们取消注释后调用。算法思路是:commandSoldiers 用 for 遍历所有士兵(可用 hero.findByType("soldier") 精确取士兵),每个士兵 findNearestEnemy() 找到敌人就 command(soldier, "attack", enemy);pickUpNearestCoin 则 findNearest(findItems()) 找到金币就 move 过去。定义好后,在循环里依次调用 summonSoldiers()、commandSoldiers()、pickUpNearestCoin()。
# 关键片段:亲手定义两个函数 def commandSoldiers(): for soldier in hero.findByType("soldier"): enemy = soldier.findNearestEnemy() if enemy: hero.command(soldier, "attack", enemy) def pickUpNearestCoin(): coin = hero.findNearest(hero.findItems()) if coin: hero.move(coin.pos)
commandSoldiers() 和 pickUpNearestCoin() 用 # 注释掉了。你把函数定义写好后,一定要记得删掉这两行前面的 #,否则函数虽已定义却从没被调用,士兵不会作战、金币也不会去捡。
hero.findByType("soldier") 只挑出"士兵",而 hero.findByType("peasant")[0] 则取出第一个花匠。相比 findFriends()(所有盟友),findByType 能精确锁定某一类单位,让"指挥士兵"这件事不会误伤或漏掉其他角色。
名字() 调用它。summonSoldier 而非 summonSoldier(),函数不会运行。# 注释掉了,要记得删掉 #。def 下面的代码必须缩进;不缩进会报错或不属于该函数。def 名字() 后面必须有冒号 :,否则语法错误。while 循环中,别层层嵌套。summonSoldier 里应是 gold >= costOf(...),够钱才召唤。恭喜你把编程能力提升到了"下一个等级"——你学会了函数!这一单元你理解了函数的核心思想:把一段"做某件事"的代码用 def 打包命名、用缩进划定函数体,并牢牢记住了它最关键的两个特性——定义时代码只保存、不执行,必须用"函数名 + 括号"调用后才真正运行。你把"捡币、召唤、指挥"三项行为各自封装成一个函数,让主循环变成一份清爽的行动清单,还在"关键力量"和"双生花"两关里亲手定义并调用了自己的函数。来回顾一下核心概念:
| 核心概念 | 关键点 | 对应关卡 |
|---|---|---|
| def 定义函数 | def 名字(): 头部四零件 | 两关 |
| 缩进函数体 | 缩进的代码块才属于函数 | 两关 |
| 延迟执行 | 定义只保存,调用才运行 | 关键力量 |
| 函数调用 | 函数名 + () | 两关 |
| 循环内调用 | while 里点名,反复执行 | 两关 |
| 封装重复行为 | 一个函数只做一件事 | 两关 |
| 取消注释调用 | 删掉 # 才会被调用 | 双生花 |
| findByType 取单位 | 精确挑出某一类角色 | 双生花 |
懂得把杂乱的行为封装成一个个命名函数、让主循环清晰如目录的英雄,才是云峰之上真正优雅的程序员。
继续向雪山之巅挺进吧,勇敢的程序员!🏔️🔑🌸
扣哒世界 编程知识 · 计算机科学 5(安息云峰):第一单元
扣哒世界教研团队