知:聊聊成语
说到成语,你一定不陌生吧?从小到大,我们学习过很多成语,比如“开天辟地”、“千钧一发”,“三天打鱼,两天晒网”——成语有很大一部分是从古代传承下来的,每个代表一个故事或典故,往往几个字就能表达非常丰富的含义,能够大大增强汉语的表达能力,可以说是古人留给我们的宝贵文化遗产。
掌握更多的成语,往往让你说话时有“出口成章”的效果;如果用在写作中,则有言简意赅、“锦上添花”的妙用——但汉语中现存的成语有五万条之多,我们总不能拿一本成语词典来死记硬背吧?当然不是,学习成语最好的方法是增加阅读量,在博览群书的过程中留意一下相关成语的用法,必要时去翻词典了解一下出处即可;另外,你还可以和朋友玩一下“成语接龙”游戏,一方说一个成语,另一方要说出以这个成语的最后一个字开头的成语,如此往复。要赢得这种游戏,就看谁的成语储备量更大了。在做游戏的同时增加了自己掌握的成语,何乐而不为呢?
本节我们就实现一个可以帮助你学习成语的程序:
- 输入一个成语,程序帮你查出这个成语的释义;
- 依靠自己的成语储备与你玩接龙游戏,一决高下。
思:程序设计
要实现本节预定的目标,你需要一个数据源来存储尽可能多的成语和释义。如果将来你学了Python等编程语言,你知道最好是使用数据库,那么在Scratch中怎么办呢?Scratch 不支持数据库,但它提供的“列表”正好能够满足你的需求,单个Scratch列表可以存储20万条数据,而且能够批量导入,真是太强大了!(本书附带的资源中包括了一万三千余条的成语和它们的解释,并保存成了Scratch能够操作的格式,拿走不谢)
有了成语和释义这两个列表,要实现前面说的功能就不难了,释义就是从成语列表中查找,然后在另一个和它顺序完全一致的释义列表中获得对应解释;而接龙游戏则是根据你输入的成语进行查找和回复。这个功能的处理流程相对复杂一点,我们可以使用前面学过的流程图工具来分析一下它的处理逻辑:
有了流程图,再写代码会容易一些。相对来说,这个程序的角色设计就简单了:
行:编程实现
1、我们先来建立一个名称为“成语库”的列表,在舞台区新建的列表上点击鼠标右键,会弹出导入和导出菜单。我们选择导入,并浏览电脑上的文件,找到随书资源中的文本文件(txt文件),名字是“成语列表”,注意,不要手工编辑文件的内容!原因我们下面再讲。
几乎瞬间,你的列表就填充了13000条成语!从列表下方的“长度”你会看到这个列表数据条数。接下来用同样的方法建立一个“释义库”列表,并导入释义数据。还是要注意,不要手工调整列表中的数据!
2、在指令区,取消“成语库”和“释义库”前面的复选框,让它们隐藏起来。然后添加两个Scratch3.0内置的角色“Button2”到舞台上,并使用绘图编辑器给它们添加上“查成语”和“玩接龙”文字:
3、为“查成语”按钮编写代码,让它在被点击时,发消息给小猫:
4、小猫收到消息,执行下面的指令来查找成语:
这里的指令相当简单,几乎不需要解释,唯一让人不太容易理解的是下面这条指令积木:
这里,为什么是“释义库”列表取“成语库”的第一个“回答”的编号项呢?由于释义列表和成语列表中的数据是一一对应的,只要在成语库列表中找到的成语编号,一定是释义库列表中对应的释义的编号,把这个编号的释义提取出来就可以了。而“连接”指令是为了用括号把成语、释义分开更容易阅读(这就是不能手工编辑“成语库”和“释义库”列表的原因,破坏了两个列表的对应关系,查出来的数据就是错误的)。
5、现在测试一下,点击“查成语”按钮,小猫提示你输入要查询的成语,输入“三心二意”,小猫会说“找到啦”,然后给出“三心二意”这个成语的意思。从它给出的释义你可以看出,我们用的“连接XX和XX”指令是怎么把你的回答和释义组合到一起,用一种比较容易接受的方式显示出来的。
6、接下来要开发第二个功能“玩接龙”了。在“玩接龙”按钮角色的代码中添加如下指令:
7、建立一个“接龙列表”,用来保存小猫和你PK的记录,再建立“成语开头”、“当前成语”、“回答数量”、“小猫答案”、“游戏结束状态”和“列表编号”六个变量,取消它们在舞台上的显示。然后为小猫编写处理“玩接龙”的指令:
这段指令的主要作用是做一些接龙开始时变量恢复初始值的工作,这样可以确保程序任何时候反复执行都不会出现意外的数据(这叫做程序的“可重入”性)。我们将游戏结束状态设置为0,表示不在结束状态;将回答数量设置为0,在下面的指令中重新计数,将当前成语设置为成语库中一个随机的条目
想一想,为什么我们用成语库的项目数,而不直接在1到13000中取一条呢?这是为了在未来你可能在成语库中添加数据,变成了13001条的时候,不至于在程序中找这条指令修改数量,避免不必要的维护工作。一条指令容易改,如果程序中多处用到了这个数字,改起来就会特别麻烦。在软件工程师的术语,这种情况叫做“发散式修改”,说明你的代码出现了“坏味道”,是我们要避免的情况之一。
8、接下来,我们用一个循环来实现接龙逻辑。这个循环会一直运行,直到“游戏结束状态”变为1,这是在你答错成语,或小猫无法找到符合条件的成语时才出现的,在循环过程中动态修改。
这段指令中,我们将“成语开头”变量设置为“当前成语”的最后一个字符,由于成语的字数不固定,所以我们不能直接用数字,而是取第“字符数”个字符,这样说听起来别扭,但实际就是取最后一个字。然后向你询问以这个字开头的成语,如果你的回答在成语库中找不到,说明你的答案不是成语(也可能这个成语比较生僻,在成语库列表中没有,不过我们暂不考虑这种小概率事件)。如果能找到,说明你的回答是成语,我们还需要进一步的判断。
9、如果你回答的内容是成语,我们要执行以下指令(这段指令应该放在图10中“如果”条件成立的空白处):
这段代码会判断你提交的回答是不是以当前成语的最后一个字开头的,如果不是,游戏也会结束,如果是,就把它加入到接龙列表,同时将回答数量+1,然后小猫用“查找成语”指令继续找你回答内容最后一个字开头的成语——这个“查找成语”是一个“自制积木”,需要把查询成语的第一个字作为条件(称为关键字)传递给它:
查询成语的过程其实就是循环判断“成语库”这个列表中的每一项,看它的第一个字符是不是和关键字相同。找到了就将“小猫答案”变量设置为这一项,退出循环,如果循环了一遍(成语库的项目数),说明没找到,就把“小猫答案”这个变量设置为“没找到”返回到调用自制积木的程序。主程序会根据结果判断下一步的逻辑——如果是没找到,那说明你赢了,如果找到了,将“当前成语”设置为“小猫答案”,继续进行下一轮主循环。
(这种方法叫做“遍历”,是效率很低的一种查找方式,在运气最差的情况下,你得把列表中的成语全过一遍,也就是循环13000次。不过谁让Scratch没有更强的查找指令呢?还好,计算机的运行速度就是快,你基本不会觉得这一万多条数据的查找超过一秒钟的)。
10、如果主循环跳出,说明游戏结束。这里我们添加指令,让小猫说出你回答出了几条成语,最后隐藏接龙列表,这一轮PK结束。
11、程序主逻辑已经完成,由于这段接龙程序比较长,这里不显示全部代码的截图了。建议你反复运行几次,看看有没有什么地方疏漏,让程序存在逻辑错误(Bug)。最后,你还可以导入一个好看的背景导入进来,并合理安排一下角色的位置,你的“成语大师”就完工了,不妨和它挑战一下,看你能不能打败自己创造的“人工智能”?
悟:总结与拓展
完成了本节课“成语大师”作品,你是否有一种“用工具创造工具”的创造性体验?不过在兴奋之余,你不妨考虑一下下面的问题:
- 有时你要查询的成语可能并不完全记得住,只记得住一个字,比如“魑魅魍魉”,你只会写其中一个字,这种情况下你怎么才能让程序替你找到想要的成语?
- 很多词典类软件在程序启动的时候会出现“每日一练”,就是从成语库中随机选择一条成语和它的释义,显示给使用者。你能实现这个功能吗?
- 其实这个程序还存在一个需要改进的地方,在查找以某个字开头的成语时,它只会寻找以这个字开头的第一个成语(因为我们要节约运行时间,这时直接就退出了循环)。比如小猫出题“席地而坐”,你回答“坐不安席”,小猫下次找成语还是会找到“席地而坐”,这样你们两个之间的游戏就进入死循环了。有没有办法让小猫每次查找同一个字开头的成语时,出现的结果不同呢?这样才符合实际情况啊。
如果你能深入思考上面的问题并自己动手实现,你对Scratch中列表的相关操作会更加熟练,直至得心应手的境界!