剥开编程教学中“知识”的坚硬果壳
2023-01-30张永顺江苏省清江中学
张永顺 江苏省清江中学
胡连国 山东省利津县高级中学
王爱胜 山东省青州第一中学
由于信息科技课程中算法与程序设计教学、人工智能等内容与传统文化知识差异很大,所以学习起来有一定的难度。因此,教师需要在教学中尽最大可能去剥开这些“知识”的坚硬果壳,才能让学生吃到里面的果肉,吸收其营养,增长智慧。
● 发现:从Python的“bug”认识数据计算精度
数据的计算精度,其背后的原理是二进制。以往,在C、C++、Pascal等语言中是需要进行数据类型定义的,而在Python中相对粗犷,这就容易出现误会。可这也为在发现问题中认识知识提供了机会。
1.案例:发现了多余的小数
一天,在讲授运算符与表达式时,为了让学生熟悉运算符、操作数,我给他们布置了一个小练习:利用Python内置的idle编辑器尝试进行一些常见的数据运算,如加、减、乘、除,在给定几个例子之外,我也请同学们发挥自己的主观能动性,多尝试几组数据。当布置完任务之后,学生们都积极地投入到数据计算的测试之中。这时,一个平时比较活泼的学生在测试了几组数据之后,突然举手,我示意他站起来。他站起之后大声说:“老师,我发现了一个Python程序的bug。”此时,班内气氛顿时热烈起来。
“哦,是什么bug呢?能不能给我演示一下?”
“老师,是这样的,当我使用16.888乘以100时(如图1),结果多了一个小尾巴!”
图1
见此情景,我立即表扬了这位学生的积极探索,并趁热打铁给学生们深入解释:①在Python中使用两个浮点型的数据进行计算时,会发现某些时候计算并不准确,如1.1+2.2结果是3.3000000000000003。这主要是二进制无法精确表达十进制小数的结果。例如11.11,整数部分按权展开1*2**1+1*2**0=(3)10,小数部分1*2**-1+1*2**-2=(0.75)10,可见二进制小数能精确表达十进制小数。②反过来,如果把十进制中的小数部分转为二进制,把该小数不断乘2、取整,直至没有小数为止,发现有时候并不能乘尽。也就是有的小数其实并不能精确地转换成二进制的小数,限于数据类型的长度,只能近似地表达,如(0.45)10≈(0.0111001……)2无法精确表达。因为在计算十进制小数时要先转化成二进制再进行计算,又因为二进制小数无法精确表达某些十进制小数,此时Python只能尽量保证精度。如果要求特别精确,在Python语言中需要用到decimal,这是Python内置标准库,用它可以进行精确浮点数运算。
总之,这位细心的学生确实发现了Python运算的“bug”,通过分析探索,学生更深刻地了解了计算机进行计算的本质,即数据类型、数据计算、二进制等知识。
2.案例反思
学生在遇见问题时有好奇心,能够去思考与探索,这些发现不论大小都能够增长学生的智慧,意义重大。在数据计算精度问题的研究中,还可以鼓励学生搜索计算机语言在相关数据类型方面的规范,甚至尝试其他计算机语言的简单计算,构建更丰富的认知空间。
● 质疑:有没有可以改变的步长
敢于对现象提出问题,正是思考的表现;敢于对知识提出质疑,正是主动认知的过程。例如,在循环中较少学生能够提出range()的意义,大都局限在对其初值、终值、步长的思考。当有部分学生提出计数循环的步长是否可以更改时,教师可以鼓励学生探索,使其在探索中深化对列表函数特点的认知。
1.案例:range()中的步长在循环中可变吗?
“老师,range()是什么?”一个学生曾这样问我。我解释它就是一个列表函数。他又问怎么列表?我说:“在循环里不是讲了吗?”他说:“可老师讲的是循环范围,我不理解这是什么函数,为什么还自己产生这么多数呢?什么从初值开始,又到不了终值,步长还默认是1,好绕!”我鼓励道:“你试着改改各个参数,观察运行结果,在循环程序里体会!”过了一会儿,他又问道:“我试了试,步长可以是变量吗?可是放在里面不变能呢?”(他给我看的程序如图2所示)
图2
我问道:“这说明什么?range()有什么特点?”他说:“步长不受后面变化的值影响?”我说:“总结一下就是range()一次性先完成了。”(学生让我给再讲讲,我没有细讲,而是给了他如图3所示的几个程序,让他试验,并要求他试完之后总结:①list是做什么的?②m的测试跟循环有什么不同?③range()、list(range())的数据类型分别是什么?)
图3
后来,他又问道:“range(1,30,0.5)能否构成小数列表?”我解释道:“调试与试验是个法宝,以后学了while循环可以考虑用追加方法构造一个小数列表,到时也可以做自己的自由变化的步长了。”
2.案例反思
学生对函数的理解多数是单一的映射数值,并没有多数据列表的认知,因此也可适当从集合角度去理解。range()值的特点、数据产生时间、整数特征等都可以在试验中体会,可举例证明。问题探究可以延伸出程序功能的升级实现,即学习while循环提高自由设计的思想。
● 类比:从书卡理解列表的应用
列表是Python的重要知识,大量的程序设计基于列表进行。但学生还没有学习数学的“数列”,因此也无法建立数据集合的概念,所以,使用实际生活中的事物类比是破解难题的重要方法之一。
1.案例:书卡抽屉与列表的类比
有一天,一位学生欢喜地进门就喊道:“老师,我太想你了!我做梦都在编程序。”我赞道:“这说明你快成明白人了,就像学车做梦就快出手了。”学生说道:“可是梦里我什么都不会,梦见一抽屉的卡片上都是你演示的一段段的程序代码,可就是看不清楚,都急死了!”我笑了,我是提过老图书馆抽屉里的卡片,但是我说的是贴个变量X、Y的标签而已,哪有一大段代码。正好当天的课上我要让学生练列表,因此给学生提出了分步探索的要求:①根据代码样式,补充真实内容完成自己的程序。②每个环节的程序单独从云课堂提交作业。
引导类比方式的探索如下:
(1)一抽屉的书卡是什么?(列表赋值)有多少张?(列表长度)哪张是第一张?(列表位置)程序如图4所示。
图4
(2)随意抽出一张书卡是哪本书?(直接访问列表数据)(删除列表数据)程序如下页图5所示。
图5
(3)怎样全部查看书卡?(顺序访问列表数据、判断数据)程序如图6所示。
图6
(4)有快速查找一本书的“方法”吗?(列表索引位置的‘方法’)程序如图7所示。
图7
(5)拿出一摞卡片(列表切片),列表有很多用途,相应地有很多操作方式,还有列表排序list.sort()、列表切片list[x:y]等,学以致用是最好的学习。程序如图8所示。
图8
2.反思与总结
概念厘清,即一个概念及应用要列出1、2、3,搞清晰、有体验,就算再做梦也不容易迷糊。操作都与需求相联系。没需求就不用发明这个操作了,所以编程也好、软件也好,一个操作方法必然是对应着应用的,有什么用要对上号,自然就容易掌握。别强记规则,而是理解应用为宜。
● 比喻:彰显自定义函数的神奇
比喻是最易理解的知识描述方式,因为它会更形象、生动、有趣,如把自定义函数比喻成自我DIY一个阿拉丁神灯,体现出可以“复用的代码”的形象来。
1.案例:DIY一个阿拉丁神灯
每到“可以复用的代码——自定义函数”这部分内容,我心里总是有个挣扎的声音:内容太抽象了,学生理解起来太困难了,不上好像也不影响学业水平测试的结果,直接跳过吧!研读课标与教材,发现程序中的自定义函数就是要让学生理解:①为什么需要自定义函数?②自定义函数怎么写?③自定义函数如何使用?我突发奇想,要不通过“DIY阿拉丁神灯”来讲解这个自定义函数?
(1)视频导入。播放“阿拉丁神灯”实现愿望的视频片段,学生需要关注那盏“灯”的作用是什么?“灯神”住在哪儿?有什么作用?“灯神”是如何被唤醒的?
(2)程序观察。向学生展示一段程序,提出问题:该程序实现什么功能?连续执行三次make_juice()能实现什么功能呢(如图9)?
图9
学生通过阅读程序回答“make_juice( )是制作苹果汁的流程,连续执行三次make_juice( )可实现制作三杯苹果汁”等。学生通过代码阅读学会了自定义函数的定义和调用方法;通过观察输出的结果,明白了调用的作用是什么。继续提出思考问题:这个程序能够实现制作苹果汁、香蕉汁、猕猴桃果汁各一杯吗?
(3)模仿修改。模仿:在课堂教学中,学生通过复制制作苹果汁函数,并将苹果修改为香蕉和猕猴桃,调试运行,发现结果都是猕猴桃。在学生发现此路不通后,我适时提出了问题:make_juice( )函数被调用的时候,知道自己是做什么的吗?如果不知道,该如何告知呢?
(4)增加参数。通过make_juice(‘苹果’)语句告知我们要制作的是苹果汁,这是在“召唤灯神”。①“神灯”设计。函数make_juice( )该如何接收“苹果”这个值呢?通过增加参数,即用make_juice(fruit)形式,参数“fruit”用来接收水果名称。②“神灯”使用。“灯”在接收到fruit参数后,在函数内部该如何来使用呢?以“将苹果去皮”为例,通过将具体的水果名称替换为变量“fruit”,将其修改为“将”+fruit+“去皮”,即可实现内部的名称自适应功能。由此,make_juice(fruit)函数可以实现多种果汁制作了,这是“灯神”可以满足不同要求的要诀。具体程序如图10所示。
图10
2.案例反思
通过“神灯”设计,修改自定义函数的参数是一种良好的“适用不同对象”的“召唤灯神灯”的代码复用。还可以继续探讨使用“循环结构”对实现某个功能的基本语句的重复式复用。自定义函数能够把多次重复的功能抽象成固定符号表达,也属于结构化程序模块设计,在程序中通过调用函数名称和传递参数达到简洁、灵活的功能现实。