马金林,马自萍
(1.北方民族大学 计算机科学与工程学院,宁夏 银川 750021;2.北方民族大学 数学与信息科学学院,宁夏 银川 750021)
自周以真[1]教授定义的计算思维被广泛认可以来,计算思维教育得到国内各高校的重视,经过广大计算机教育工作者不遗余力地推广,正被越来越多的高校实践。
程序设计课程作为一门专业基础课程,为计算机专业学生学习后续的数据结构、操作系统、面向对象程序设计、编译原理、软件工程等课程奠定了基础,是一门将理论与实践紧密结合的课程。民族高等学校作为民族高等教育的主要力量,肩负培养民族高等教育人才的特殊使命,然而,由于民族高等学校的特殊地位,其程序设计课程教学普遍存在诸多问题。
与国内其他高校一样,民族高校的程序设计课程教学取得了一定成果,但由于师资力量、教学水平、学生水平等问题的制约,民族高校的程序设计课程面临更多问题、存在更多不足,具体体现在5个方面。
部分教师对程序设计和计算思维的认识不够深入,认为只要让学生学会程序设计的基本知识、掌握基本的编程方法、会使用一些基本算法以及遇到问题能够解决就足够了,没必要花更多时间和心思让学生掌握分析问题和解决问题的基本思路、方法和手段。
由于民族高校所处地域和身份的差异,连普通的专任教师都很难引进,更别说高水平的教师。由于师资短缺,现有教师承担的教学工作量较大,做好日常教学已需要他们投入大量精力,鲜有教师能抽空钻研教学。
民族高校的学生多来自于民族地区,普遍为少数民族学生,再加上各地教育发展水平不同,导致学生入学时计算机水平参差不齐、相差甚远。传统的教学方法已不能满足所有学生的需要,教师无法兼顾不同能力水平的学生,导致水平较高和较差的学生对程序设计课程失去兴趣,从而严重影响教学质量和教学效果。
教学中,教师注重学生对知识的学习,忽视思维的培养和训练;注重语法的讲授,忽视算法和思维的培养;注重解题方法的讲解,忽视结题思路的分析;注重知识点讲解,忽视计算机系统知识的贯通。实验教学环节多采用验证性实验,很少采用设计类实验,学生的计算思维和创新意识无法得到充分训练。
目前的课堂教学多以讲授为主,实践教学多以验证操作为主,学生在学习过程中的参与度不高、主动性不够,常常处于被动学习状态,整个教学过程几乎没有学生思考的时间和空间,导致学生的动手能力和思考能力严重不足,综合分析问题的能力、解决问题的能力以及创新能力都很欠缺,严重束缚了学生的计算思维培养。
综上,种种教学状况造成教学效果不佳。只有直面问题,分析其产生的原因,从根本入手,转变观念、培养兴趣、发挥师生的主观能动性,才能改变程序设计课程教学的现状。引入计算思维被证明是一个不错的选择[2-3]。
周以真教授对计算思维的阐述中提到,计算思维的本质是抽象和自动化,是通过约简、嵌入、转化和仿真将复杂问题转化为简单问题的思维方法,是递归和并行处理的思维方法,是通过抽象和分解来完成庞杂任务的思维方法,是利用启发式推理寻求解答的思维方法[4]。程序设计课程恰好可以借助对实际问题的分析,选取合适的方法去解决问题,并编写成可运行的程序。
战德臣教授在“计算之树”中描述了大学生应具备的计算思维,其中适合程序设计课程培养的计算思维有算法类问题求解的思维和系统类问题求解的思维[5-6]。
程序设计强调问题空间与解空间在结构上尽可能一致,即问题的描述与其实现的解法在结构上尽量一致[7]。文献[7]将程序设计中编程解题的一般思维方法或过程概括为“观察—联想—变换”,首先通过观察,认识并理解该问题;然后通过联想,寻找该问题同已有知识和经验之间的联系;最后通过变换,把该问题转化为另一个或几个易于解决的新问题,最终达到解决原问题的目的。文献[7]认为在程序设计中,编程解题时“观察”“联想”“变换”的思想活动总是互相联系、互相影响又互相交织地进行着,形成了一个有机的整体。
由此可见,通过程序设计中的思维方法训练,可以使学生掌握基本的思维方式和方法,从而掌握并熟练运用计算思维。通过程序设计课程来培养计算思维应该包含两方面内容:一是深入理解计算机的体系结构,掌握计算机的主要原理和系统架构;二是深入掌握计算机解决问题的思路和方法,总结规律,以便更好更自觉地运用信息技术解决算法类和系统类问题。
教学环节和实践环节是程序设计课程不可缺少的两个必要过程。无论是知识传递,还是思维训练都需要在这两个过程中实施。程序设计课程的授课教师一定要清楚认识到计算思维的培养是一个长期、系统的工程,不是一两门课程能够独立完成的任务,因此,教师要树立正确的教学培养观,开展程序设计课程能够完成的计算思维能力训练和培养工作。
传统的教学环节侧重于传授知识,虽然兼顾解题思路,但是不太重视思维培养,因此,在计算思维培养的教学改革中,教师要在做好传统教学工作的同时,融入计算思维能力的训练工作。
程序设计的基本知识包括基本语句、数据类型、程序控制结构、文件操作等。授课教师在讲解基本知识时,一般需要借助程序实例进行讲解,在此过程中可以自然地嵌入提出问题、分析问题、解决问题、编写程序、运行程序、优化算法等环节,潜移默化地培养学生的计算思维能力。此外,授课教师还应将局部与整体的关系、程序思维、算法思维、构造思维、分治思维、转化思维、逆向思维、联想思维等思维方式贯穿到程序设计中,通过思维训练使学生掌握并熟练运用计算思维。
为巩固理论知识,程序设计课程还设置了实践环节,以提高学生对实际问题的分析能力和处理能力,增强其动手能力,实践环节包括课程实验和课程设计两部分内容。
课程实验以“实验指导手册”为依据,在任课教师和实验人员的指导下,学生按实验要求完成规定的实验项目。为训练学生的编程能力,培养学生独立解决问题的能力,不安排验证性实验,所有实验必须由学生自主独立完成,对遇到的问题可以讨论或查阅资料来解决。验证性实验、典型例题(习题)的调试等内容由学生自主实验进行,用以积累程序调试经验,消化课堂讲解内容。
课程设计是验证学生课程学习效果的综合设计类测试项目,课程设计旨在通过一个综合性题目考查学生综合运用所学知识的能力、分析问题的能力和解决问题的能力。
课堂讲授是每一门课程都必须设置的教学环节,教师应高效利用讲授环节,在传授基础知识的同时训练学生解决问题的思维方法。通过讲解使学生了解分析和解决问题的常用思路和方法,逐步引导其形成正确的思维方法。
每课一练为学生提供了一次练习课堂所学知识,分析并解决问题的机会。每课一练的内容为授课教师精心挑选的题目,可以是考研试题、自拟题目或竞赛题目。与课后练习不同,每课一练旨在巩固、强化并提高学生活学活用的能力和水平,进一步锻炼思维。每课一练是实践课堂中讲授解题思路和方法的一个必要手段,通过每课一练使学生掌握并巩固教师所讲授的思维方法,为其真正掌握思维方法奠定基础。
由于程序竞赛可以吸引部分学习能力和动手能力强的学生参与其中,因此鼓励学生积极参与不同难度和层次的各类型程序设计竞赛,以实例和任务的形式引导学生掌握分析问题及解决问题的方法和能力。程序设计竞赛的题目往往融入了计算机、数学、逻辑、物理等多个学科的知识,且题目均来自或模拟实际生产生活。通过这些题目,可以很好地提高学生分析问题、寻求解决方案、调整解决方案直至最终解决问题的能力,以此锻炼学生的思维能力,让其理解并掌握多种思维方式,培养计算思维能力。
计算思维教学与所有的教学活动一样,必须遵循“因材施教”的原则,直面个体差异,根据学生的计算机水平设置合适的课程内容和教学方式。
学生的个体差异包括现有计算机水平和学习能力,分层教学应据此实施。首先根据学生入学时的计算机水平进行分班,将水平相近的学生集中在一起,以针对不同层次的学生设定不同的教学内容、教学方式和考核方式;然后关注同一层次中学习能力较强的学生,实行转班或调整其学习内容,以达到因材施教的目的。
教师可根据授课进度和学生掌握程度灵活安排课后辅导,目的是了解学生的掌握程度、检查学生学习效果以及解答学生困惑,这样既能督促学生学习,又能掌握学生的学习效果,从而及时解决学生当前面临的学习问题。课后辅导可固定每周一次,也可根据学生掌握情况灵活按需安排,辅导老师可以是授课教师本人,也可以是助教。
递归是程序设计课程的必讲内容,但是如何使学生既了解递归的基本知识,又深入理解递归的深层原理,让学生融会贯通的同时训练思维,往往需要教师花费一番功夫。讲授递归往往以计算n的阶乘为例,其关系式为factorial(n)=n*factorial(n-1),其程序通常是:
为使学生理解递归,教师以流程图的形式让学生理解“函数自己调用自己”的原理,然后再通过几个递归的实例加强学生的理解。至此,递归的讲解基本结束。
然而,“自己调用自己”与学生的现有认识(人类世界没有这样的工作设定)并不一致,学生是否真正理解,尚有疑问。要想使学生彻底理解,需要让他们明白机器层面递归执行和调用的过程,即明白机器世界的工作设定。
阶乘函数编译后被放在代码段,函数被调用一次,就会在堆栈中以先进后出的方式排列栈帧,每一个栈帧记录了上一个栈帧的指针、输入参数和返回值。函数的调用过程具体方法是:当计算5*factorial(4)时,5是常数,因为不知道factorial(4)的值,所以需要产生新的栈帧来计 算 factorial(4); 而 factorial(4)需 要 factorial(3)、factorial(3)需 要 factorial(2)、factorial(2)需 要factorial(1),如此递归,得出factorial(1)=1;再每个栈帧依次出栈、进行回退,将值返回给调用它的栈帧,最终计算出factorial(5)的值。需要强调的是,递归函数必须有终止条件,避免无限递归。
至此,学生已经能更深入地理解函数执行时机器层面的内容,对其理解递归更有帮助,但以上程序还存在一个问题,即需要维护多个栈帧,其空间复杂度将会很大。有没有改善的办法?分析其原因,是由前面的函数调用关系决定的,即factorial(n)=n*factorial(n-1),每一个栈帧都需要记录下一个函数栈帧的返回值才能计算当前栈帧的结果,不可避免地使用了多个栈帧。那么,能不能做到仅使用一个栈帧?如前所述,由于要记录n和下一个函数栈帧的返回值,所以每一次函数调用都要产生一个新的栈帧,如果不记录这两个值,就不用每次调用时都产生一个新的栈帧,实现整个递归只需要一个栈帧。如此设定必须对递归算法和函数进行修改,让函数返回需要的返回值[8]。
再次分析阶乘公式n!=n×(n-1)!的实现过程,计算n!需要传递n并调用(n-1)!、计算(n-1)!时需要传递(n-1)并调用(n-2)!,依次类推……直至计算到1!时回归。能否换一种思维,在调用(n-1)!时已经有值n、调用(n-2)!时已经有值n×(n-1)、调用(n-3)!时已经有值n×(n-1)×(n-2),依次类推……调用1!时已经有值n×(n-1)×(n-2)× ×2,因将计算1!=1作为终止条件,实际上此时已经能计算出n!,那么程序还需要回退、还需要逐层返回值吗?答案自然是否定的。下面给出此算法的程序。
计算5!的过程如下,执行函数factorial(5,1),调用factorial(4,5*1),调用factorial(3,4*5),调用factorial(2,3*20), 调 用 factorial(1,2*60), 返 回 值120,至此,计算出5!的值为120,程序执行完成。
由于本函数直接传递了n值和计算结果,不存在需要记录n和下一个函数栈帧的返回值的要求,也不存在返回值和回退的情况。无论n的值有多大,本函数只需要使用一个栈帧,同一个栈帧在递归中被复用。这就是“尾递归”,即当递归调用是函数体中最后执行的语句并且它的返回值不属于表达式的一部分时,这个递归被称为尾递归。
如此,既让学生深入理解了递归,又让他们明白机器层面的知识,同时让他们明白只有理解计算机机器层面的东西才能设计出好程序。通过本次内容的讲解,让学生感受到提出问题、分析问题、解决问题、进一步提出问题、优化算法直至最终解决问题的整个过程,进行了一次很好的思维训练。
计算思维能力的培养为民族高校程序设计课程改革提供了一个发展机遇,对改善办学水平、提高教学质量和人才培养质量有十分积极的意义。
[1]Jeannette M Wing. Computational Thinking[J]. Communications of the Association for Computing Machinery, 2006(3): 33-35.
[2]高敬阳, 尚颖, 山岚. 化工类院校计算机基础教学中计算思维的培养[J]. 中国大学教学, 2014(2): 41-44.
[3]王先超, 王春生, 胡业刚, 等. 以培养计算思维为核心的C程序设计探讨[J]. 计算机教育, 2013(13): 44-47.
[4]陈国良, 董荣胜. 计算思维与大学计算机基础教育[J]. 中国大学教学, 2011(1): 7-11.
[5]战德臣, 聂兰顺. 计算思维与大学计算机课程改革的基本思路[J]. 中国大学教学, 2013(2): 56-60.
[6]战德臣, 聂兰顺, 徐晓飞. “大学计算机”:所有大学生都应学习的一门计算思维基础教育课程[J].中国大学教学, 2011(4):15-20.
[7]吴文虎, 王建德. 世界大学生程序设计竞赛(ACM/ICPC)高级教程(第1册):程序设计中常用的计算思维方式[M]. 北京:中国铁道出版社, 2009.
[8]刘欣. 张大胖学递归[EB/OL]. (2016-11-14)[2017-11-10]. http://mp.weixin.qq.com/s/YpG9TvTCBus2FK6LbArvvw.