黄金峡谷、银色沼泽、青铜盆地——三座山谷里散落着不同价值的硬币。你手下有三个农民,每人只能进自己那片区域,而每个区域只产出一种价值的硬币。麻烦来了:hero.findItems() 一次性把所有硬币都返回给你,混在同一个数组里。如果直接让农民去捡"最近的硬币",他们会瞄准错误的目标、乱作一团。你需要一件新工具,把这一大堆硬币按价值分拣成三堆,再各自交给对应的农民——这就是本单元的主题:过滤筛选(filtering)。
if 检查每个元素的属性(如 item.value)、用 append() 把合格的元素追加到新数组,再用 if/elif 一次遍历把硬币分成金、银、铜三类,最后把每个分类数组作为参数传给函数去处理。这是数据处理和 AI 里最基础、最常用的操作之一。
关卡概览定义得很清楚:"过滤是保留或去除某些元素的过程。"想象你面前有一大筐混装的硬币,你拿三个空篮子,一枚一枚检查:金币放金篮、银币放银篮、铜币放铜篮。检查完,原来乱糟糟的一筐,就变成了整整齐齐的三篮。程序里,"筐"是原数组,"篮子"是新数组,"一枚一枚检查"是循环,"是不是金币"是条件判断。
item.value)。任何过滤都由三个动作组成,缺一不可:遍历(把原数组的每个元素都看一遍)、条件(用 if 判断这个元素要不要)、收集(合格的用 append 放进新数组)。记住这三步,你就能应对几乎所有筛选任务。
遍历数组有两种常见写法。一种是 for item in items:,直接把每个元素赋给 item,简洁好读;另一种是 for i in range(len(items)): 用下标访问 items[i],当你需要下标时才用它。关卡默认代码用的是前者。
for item in items: # item 就是当前元素 ...
for i in range(len(items)): item = items[i] ...
每枚硬币都有一个 value 属性表示它的价值:金币 value == 3、银币 value == 2、铜币 value == 1。筛选的核心就是用 == 检查这个属性等于几。关卡概览还展示了另一个例子——按 enemy.type == "thrower" 筛出所有投掷手,思路完全一样,只是把"按 value 筛"换成了"按 type 筛"。
# 按属性筛选:value 或 type 都行 if item.value == 3: # 金币 ... if enemy.type == "thrower": # 投掷手 ...
过滤前,要先为每个分类准备一个空数组——用 [] 创建。关卡里在 while True 循环内,每一帧都重新建三个空数组 goldCoins = []、silverCoins = []、bronzeCoins = []。这一步很重要:每帧重置,才能装当前这一帧新扫描到的硬币,不会和上一帧的混起来。
# 每帧先备好三个空篮子 goldCoins = [] silverCoins = [] bronzeCoins = []
数组.append(元素) 把一个元素追加到数组末尾,数组随之变长。Python 用 append,而关卡概览里出现的 throwers.push(enemy) 是 JavaScript 的写法,作用相同——都是"压入一个元素"。写 Python 时统一用 append。
goldCoins.append(item) # 追加到末尾
throwers.push(enemy); // 同样是压入末尾
本关不是"筛一类",而是"分三类"。用 if / elif / elif 把三种价值分别导向三个数组:value == 3 进金币数组,value == 2 进银币数组,value == 1 进铜币数组。用 elif 而不是三个独立的 if,是因为一枚硬币只可能属于一类,匹配到就无需再检查其余分支,更高效。
关键在于:不需要遍历三遍。只在一个 for 循环里,对每枚硬币用 if/elif 判断一次,就把它投进正确的篮子。默认代码已经给好金币那一支(if item.value == 3: goldCoins.append(item)),留下银币、铜币两支让你补全。
# 一次遍历,if/elif 把硬币分进三个数组 for item in items: if item.value == 3: goldCoins.append(item) elif item.value == 2: silverCoins.append(item) elif item.value == 1: bronzeCoins.append(item)
| 扫描到的硬币 | value | 命中分支 | 进入数组 |
|---|---|---|---|
| 金币 | 3 | if | goldCoins |
| 铜币 | 1 | elif(1) | bronzeCoins |
| 银币 | 2 | elif(2) | silverCoins |
| 金币 | 3 | if | goldCoins |
elif,一旦命中就跳过后续判断,省去无用比较;若写成三个独立 if,每枚硬币都会白白多比两次。逻辑虽也对,但不够优雅高效。
分拣完成后,三个数组各自交给对应的农民去捡。这里用了一个接收"农民 + 硬币数组"两个参数的函数 commandPeasant(peasant, coins):它在 coins 里找离该农民最近的硬币,命令农民移动过去。把整个数组当参数传进去,函数内部就能只在这一堆硬币里搜索,绝不会跑去捡别区的硬币。
# 函数接收一个农民和一个硬币数组 def commandPeasant(peasant, coins): coin = peasant.findNearest(coins) # 只在这堆里找 if coin: hero.command(peasant, "move", coin.pos)
findNearest 会返回 None。所以 command 前一定要用 if coin: 确认真的找到了硬币,否则命令一个"不存在的目标"会报错。
三个农民有各自的名字:Aurum(金)、Argentum(银)、Cuprum(铜,正是三种金属的拉丁名)。关卡用一个字典把名字映射到具体农民,取用时清晰明了。这样"金币数组交给 Aurum、银币数组交给 Argentum、铜币数组交给 Cuprum"就一目了然,把"分类的数据"和"处理的对象"整齐地对应起来。
# 用字典给三个农民起名对应 friends = hero.findFriends() peasants = { "Aurum": friends[0], # 金 "Argentum": friends[1], # 银 "Cuprum": friends[2] # 铜 } # 分类数组各自交给对应农民 commandPeasant(peasants.Aurum, goldCoins) commandPeasant(peasants.Argentum, silverCoins) commandPeasant(peasants.Cuprum, bronzeCoins)
这一节聚焦关卡的策略、思路与算法设计,只展示用于说明的关键片段,完整通关程序请你自己动手完成。
黄金峡谷、银色沼泽、青铜盆地三座山谷分别只生成金币(value 3)、银币(value 2)、铜币(value 1)。三个农民 Aurum、Argentum、Cuprum 各守一区,拿不到其他区域的硬币。目标是命令每个农民去捡"自己区域里最近的硬币",绝不能让他们瞄准错误的目标。
核心难点在于 findItems() 把所有硬币混在一个数组里返回。如果直接让农民对整个数组 findNearest,他可能瞄上别区的硬币而白跑一趟。正确策略分两步:第一步过滤——遍历所有硬币,按 value 分进金、银、铜三个数组;第二步分派——把每个分类数组连同对应农民传给 commandPeasant,让农民只在属于自己的那堆里找最近的。
这一关要你补两处。其一,补全过滤的分类:默认代码已写好金币分支 if item.value == 3: goldCoins.append(item),你需要照着加上 elif item.value == 2: silverCoins.append(item) 和 elif item.value == 1: bronzeCoins.append(item)。其二,补全 commandPeasant 函数体:用 peasant.findNearest(coins) 在传入的数组里找最近硬币,若找到就 hero.command(peasant, "move", coin.pos)。注意整个逻辑放在 while True 里,每帧重新扫描、重新分拣,因为硬币会不断刷新。
# 关键片段:每帧重扫、分拣、分派 while True: items = hero.findItems() goldCoins = [] silverCoins = [] bronzeCoins = [] # 一次遍历分三类(银、铜两支需你补全) for item in items: if item.value == 3: goldCoins.append(item) elif item.value == 2: silverCoins.append(item) elif item.value == 1: bronzeCoins.append(item) # 分好的数组各自交给对应农民 commandPeasant(peasants.Aurum, goldCoins) # ...银、铜同理
= [] 必须写在 while 循环内部。若写在循环外,数组只会越积越多,混入早已被捡走的旧硬币,导致农民追逐已经不存在的目标。放在循环内、每帧清空重装,才能始终反映当前场上的硬币。
commandPeasant(peasant, coins),只需换传入的农民和数组,就能复用同一段代码——这正是"过滤 + 函数封装"的组合威力:过滤负责把数据分好,函数负责统一处理每一份。
goldCoins 传给金区农民、silverCoins 传给银区农民,绝不能把没过滤的 items 传进去,否则农民会盯上够不着的别区硬币,原地空转。
goldCoins = [] 等必须在 while 内每帧重建,否则会不断堆积旧硬币。if 判断却没把元素加进新数组,过滤结果永远是空的。append(item),别误写成 append(item.value) 或索引。==;写成 if item.value = 3 是赋值,会语法报错。if/elif。findNearest 返回 None,需先 if coin: 再命令。items 或错误分类传给农民,导致他瞄准别区目标。恭喜你打通了资源谷!这一单元你掌握了数据处理里最常用的一项技能——过滤筛选。你理解了过滤"保留或去除某些元素"的本质,牢记了它的三要素:遍历、条件、收集;你学会了用 for 遍历原数组、用 if 检查 item.value 这样的属性、用 append() 把合格元素追加进新数组;更进一步,你用 if/elif 在一次遍历里把硬币分成金、银、铜三类,再把每个分类数组作为参数传给 commandPeasant 函数,配合字典把三个农民和三类硬币整齐对应。过滤 + 函数封装 + 字典管理,三者组合让"给对的人分对的数据"变得清晰而高效。来回顾一下核心概念:
| 核心概念 | 关键点 | 对应内容 |
|---|---|---|
| 过滤的本质 | 保留/去除符合条件的元素 | 10.1 |
| 过滤三要素 | 遍历 → 条件 → 收集 | 10.1 |
| 遍历数组 | for item in items | 10.2 |
| 按属性筛选 | if item.value == 3 | 10.2 · 资源谷 |
| 新建空数组 | 循环内每帧 [] 重建 | 10.3 · 资源谷 |
| append 追加 | 数组.append(元素) | 10.3 |
| if/elif 多路分类 | 一次遍历分三类 | 10.4 · 资源谷 |
| 数组作参数 | 把分类数组传给函数 | 10.5 · 资源谷 |
| 字典管理单位 | 名字映射到农民 | 10.5 · 资源谷 |
| findNearest 判空 | if coin: 再命令 | 10.5 · 资源谷 |
懂得把杂乱的数据按条件分门别类、再把每一份精准交给对的处理者的英雄,
才是云峰之上真正条理分明的程序员。继续向雪山之巅挺进吧,勇敢的程序员!🏔️⛏️💰
扣哒世界 编程知识 · 计算机科学 5(安息云峰):第十单元
扣哒世界教研团队