对于一个有志于投身信息技术领域的人员来说,程序设计是一门和计算机硬件与软件息息相关的学科,是计算机诞生以来一直蓬勃发展的新兴科学。从长远的发展来看,程序设计能力已经被看成是国力的象征,不少省市已经将程序设计列入高中学生的选修课程,有些省份的高考甚至加入程序设计的考核,例如浙江省从2017年高考开始,除了语文、数学、英语三科必考外,其他七选三的选考科目中就有“技术”科目,考核的内容包括通用技术和信息技术。程序设计能力不再是计算机、通信、信息等相关理工科专业的学子必备的能力,而是新一代人才都必须具备的基本能力。程序设计的本质是数学,或者更直接地说是数学的应用。过去,程序设计非常看重计算能力。随着信息与网络科技的高速发展,计算能力不再是唯一的目标,在程序设计课程中着重加强学生计算思维(Computational Thinking,CT)的培养和训练。
在日常生活中,无论是大事还是小事,都是在解决问题,任何只要牵涉到“解决问题”的议题,都可以运用计算思维来解决。读书与学习就是为了培养我们在生活中解决问题的能力,计算思维是一种利用计算机的逻辑来解决问题的思维,就是一种能够将问题“抽象化”与“具体化”的能力,也是新一代人才都应该具备的素质。
我们可以这样说:“学习程序设计不等于学习计算思维,程序设计的过程就是一种计算思维的表现,而要学好计算思维,通过程序设计来学绝对是最佳的途径”。程序设计语言本身就只是工具,没有最好的程序设计语言,只有是否适合的程序设计语言。学习程序设计的目标绝对不是要把每位学习者都培养成为专业的程序设计人员,而是要帮助每个人建立系统化的逻辑思维模式。
2006年,美国卡内基·梅隆大学的Jeannette M.Wing教授首次提出了“计算思维”的概念,她提出计算思维是现代人的一种基本技能,所有人都应该积极学习,随后谷歌(Google)公司也为教育者开发了一套计算思维课程(Computational Thinking for Educators),这套课程提出了培养计算思维的4部分,分别是分解(Decomposition)、模式识别(Pattern Recognition)、模式概括与抽象(Pattern Generalization and Abstraction)以及算法(Algorithm)。虽然这并不是建立计算思维唯一的方法,不过通过这4部分我们可以更有效地进行思维能力的训练,不断使用计算方法与工具解决问题,进而逐渐养成我们的计算思维习惯。虽然这并不是建立计算思维的唯一方法,不过通过这4部分我们能更有效率地拓展思维,提高使用计算方法与工具解决问题的能力,最终建立计算思维。也就是要将这4部分进行系统的学习与组合,并使用计算机来协助问题的解决(参考图1-2)。在后面的章节我们来详细说明。
图1-2
许多人在编写程序或解决问题时,对于问题的分解(Decomposition)不知道从何处着手,将问题想得太庞大,如果对一个问题不能进行有效分解,就会很难处理。将一个复杂的问题分割成许多小问题,把这些小问题各个击破,小问题全部解决之后,原本的大问题也就解决了。
下面我们以一个实际的例子来说明。如果有8幅非常难画的图,我们可以把它们分成2组各4幅图来完成,如果还是觉得太复杂,继续再分成4组,每组各两幅图来完成,采用相同模式反复分割问题(如图1-3所示),这就是最简单的分治法(Divide and Conquer)的核心思想。
图1-3
分治法(也称为“分而治之法”)是一种很重要的算法,我们可以应用分治法来逐一分解复杂的问题,它的核心思想是将一个难以直接解决的大问题按照相同的概念分割成两个或更多的子问题,以便各个击破,即“分而治之”。其实任何一个可以用程序求解的问题所需的计算时间都与其规模有关,问题的规模越小,越容易直接求解。
例如我们有一台计算机的部件出现故障了,如果将整台计算机逐步分解成较小的部分,对每个部分的各个硬件部件进行检查,就容易找出有问题的部件。再例如一位警察在思考如何破案时,需要将复杂的问题细分成许多小问题。经常编写程序的人在遇到问题时会考虑所有的可能性,把问题逐步分解,久而久之,这种逻辑思维的习惯就成了这些人的思考模式了,如图1-4所示。
图1-4
在把一个复杂的问题分解之后,我们常常可以发现小问题中有共同的属性以及相似之处,在计算思维中,这些属性被称为“模式”(Pattern)。模式识别是指在一组数据中找出特征(Feature)或规则(Rule),用于对数据进行识别与分类,以作为决策判断的依据。在解决问题的过程中找到模式是非常重要的,模式可以让问题的解决更为简化。当问题具有相同的特征时,它们能够被更简单地解决,因为存在共同模式时,我们可以用相同的方法解决此类问题。
举例来说,在知道怎么描述一只狗之后,我们可以按照这种模式轻松地描述其他狗,例如狗都有眼睛、尾巴与4只脚,不一样的地方是每只狗或多或少地有其独特之处(如图1-5所示),识别出这种模式之后,便可用这种解决办法来应对不同的问题。因为我们知道所有的狗都有这类属性,当想要画狗的时候便可将这些共同的属性加入,这样就可以很快地画出很多只狗。
图1-5
模式概括与抽象在于过滤以及忽略不必要的特征,让我们可以集中在重要的特征上,这样有助于将问题抽象化,通常这个过程开始会收集许多数据和资料,通过模式概括与抽象把无助于解决问题的特性和模式去掉,留下相关的以及重要的共同属性,直到我们确定一个通用的问题以及建立如何解决这个问题的规则。
“抽象”没有固定的模式,它会随着需要或实际情况而有所不同。例如,把一辆汽车抽象化,每个人都有其各自的分解方式,像是车行的业务员与修车技师对汽车抽象化的结果就可能会有差异(如图1-6所示)。
车行业务员:轮子、引擎、方向盘、刹车、底盘。
修车技师:引擎系统、底盘系统、传动系统、刹车系统、悬吊系统。
图1-6
算法是计算思维4个基石中的最后一个,不但是人类使用计算机解决问题的技巧之一,也是程序设计中的精髓,算法常出现在规划和设计程序的第一步,因为算法本身就是一种计划,每一条指令与每一个步骤都是经过规划的,在这个规划中包含解决问题的每一个步骤和每一条指令。
在日常生活中有许多工作可以使用算法来描述,例如员工的工作报告、宠物的饲养过程、厨师准备美食的食谱、学生的课程表等。如今我们几乎每天都要使用的各种搜索引擎都必须借助不断更新的算法来运行,如图1-7所示。
图1-7
在韦氏辞典中,算法定义为:“在有限的步骤内解决数学问题的程序。”如果运用在计算机领域中,我们也可以把算法定义成:“为了解决某项工作或某个问题,所需要有限数量的机械性或重复性的指令与计算步骤。”
在计算机中,算法更是不可或缺的重要一环。下面讨论的内容包括计算机程序常涉及算法的概念和定义。在认识算法的定义之后,我们再来看看算法所必须符合的5个条件(参考图1-8和表1-1)。
图1-8
表1-1
在认识算法的定义与条件后,我们接着来思考:该用什么方法来表达算法最为适当呢?其实算法的主要目的在于让人们了解所执行的工作的流程与步骤,换句话说,算法是描述如何解决问题的办法,因而只要能清楚地体现算法的5个条件,即可清晰地表达算法。
常用的算法一般可以用中文、英文、数字等文字来描述,也就是使用文字或语言语句来说明算法的具体步骤,有些算法则是使用可读性高的高级程序设计语言(如Python、C、C++、Java等)或者伪语言(Pseudo-Language)来描述或说明的。
伪语言接近于高级程序设计语言,是一种不能直接放进计算机中执行的语言。一般需要通过一种特定的预处理器(Preprocessor)或者通过人工编写转换成真正的计算机语言才能够加载到计算机中执行,目前较常使用的伪语言有SPARKS、PASCAL-LIKE等。
流程图(Flow Diagram)是一种相当通用的算法表达方式,就是使用某些特定图形符号来表示算法的执行过程。为了让流程图具有更好的可读性和一致性,目前较为通用的是ANSI(美国国家标准协会)制定的统一图形符号。假如我们要设计一个程序,让用户输入一个整数,而这个程序可以帮助用户判断输入的这个整数是奇数还是偶数,那么这个程序的流程图大致如图1-9所示。
图1-9
算法和过程(Procedure)有何不同?算法和过程是有区别的,过程不一定要满足算法有限性的要求,例如操作系统或计算机上运行的过程。除非宕机,否则永远在等待循环中(Waiting Loop),这就违反了算法五大条件中的“有限性”。