前言

近期在阅读和翻译一些国外文章和项目,关于编程入门和学习,这些作者的兴趣不只在技术,他们中的不少人关心教育和认知科学。

国内编程教育目前十分火热,但对于什么是好的编程入门教育,什么是好的编程入门语言,大家莫衷一是,铺天盖地的广告/口水文虽不少,但几乎没看到什么高质量的讨论文章,甚至没什么人真在认真思考这件事。

去年翻译了awesome-python-in-education,这是由西班牙的一位计算机科学老师quobit发起的一个项目,旨在收集Python教育方面的资源列表。

我的翻译版本为: Python教育资源大全中文版.除了翻译之外,对于列表中不熟悉的项目/内容,我都亲手试用或阅读了一遍,并给出评价和使用体验。

在这份列表里,我很喜欢Philip Guo教授写的Why Python is a great language for teaching beginners in introductory programming classes一文。他也是OnlinePythonTutor项目的发起者。

在征得Philip Guo教授的同意之后,我试着把这篇文章翻译到我博客上(还在进行中)。我同@qumin说了我的想法,@qumin给了一些建议。其中提到说:

可不可以在单纯翻译某篇文章的基础上更进一步,以某个问题或主题为导向,在阅读大量好的材料之后进行整合和创作,就像刘未鹏博客里的很多文章一样,往往根据一条主线旁征博引,博采众长(给出很多reference),这样翻译工作也就成了整个创作过程的一部分,文章容量更大,读者一站式地对该主题可以有深刻而全面的了解,可能会更有说服力,有点像文献综述(literature review)的感觉。

我也是刘未鹏的读者。刘未鹏的写作让我想起乔纳森·卡勒《文学理论入门》中说的:

从歌德、麦考利、卡莱尔和爱默生的时代开始出现了一种新型的著作,这些著作既不是评价文学作品的相对短长,也不是思想史,不是伦理哲学,也不是关于社会的预言,而是所有这些融为一体,形成一种新的体裁… 是一系列没有界限的、评说天下万物的著作,从哲学殿堂里学术性最强的问题到人们以不断变化的方法评说和思考的身体问题,无所不容。“理论”的种类包括人类学、艺术史、电影研究、性别研究、语言学、哲学、政治理论、心理分析、科学研究、社会和思想史,以及社会学等各方面的著作。

2013年大三的时候,因为读了刘未鹏为什么你应该(从现在开始就)写博客,开始了自己的博客之旅,至今,断断续续写了440篇博文,记录自己的日常、思考、技术探索和读书笔记。

在本文中,我试着按照@qumin的建议:

在阅读大量好的材料之后进行整合和创作…有点像文献综述(literature review)的感觉.

但我未必会始终:

根据一条主线旁征博引.

而有可能根据文中提到有趣观点或材料,引出思考,当然它们应该都与文章主题相关.

本文是第一次尝试。

编程作为一种思考方式

Allen Downey 去年(2017)写了一篇精彩的文章: Programming as a Way of Thinking

摘要中写道:

The power of modern programming languages is that they are expressive, readable, concise, precise, and executable.

现代编程语言的强大之处在于它们具有表现力、可读性、简洁性、精确性和可执行性正是《编程作为一种思考方式》一文的核心论点。

编程过去常常是关于转换的:用自然语言表达思想,同时使用一些数学符号,然后编写流程图和伪代码,最后编写程序。转换是必要的,因为每种语言提供了不同的潜在能力。自然语言具有表现力和可读性,伪代码更精确,数学符号简洁,代码可执行。

在过去,“编程"是一种很割裂的活动。当时的人们以及今天写FORTRAN和C之类第一代语言的人,恐怕并不这么看。如果没用过高级语言(像文中作者提到的Python),你可能觉得编程就该是这样的,在几种“语言”中转化。由于你一般用自然语言表达思想,你可能把"编程"视为末技,在你眼中,编程不过是“写代码”,思考都在编程之前进行了,编程不过是把前边已经想清楚的事(很可能已经有伪代码了) 转换为可执行的代码罢了。

这种观点,在计算机诞生之初,冯诺伊曼就表述过。《逻辑的引擎》一书里提到:

冯·诺依曼把计算机程序设计看作一种活动,把这种看法与图灵的看法作一番比较是很有趣的。冯·诺依曼称之为“编码”,并且明确指出它是一种办事员的工作,基本不需要理智参与。有一件轶事颇能发人深省,高等研究院让学生用手摇计算机把人类易读的指令翻译成机器语言,有一个极富才华的年轻人提议写一个汇编器来自动完成这种转换。据说冯·诺依曼曾经气愤地说,让一个有用的科学工具去做一项办事员的工作简直是浪费时间。

图灵的观点则要现代很多:

计算机程序设计过程应当是非常吸引人的,它不应被沦落为苦差事,因为任何非常机械的过程都可以交由机器本身去处理。

如果你使用过现代编程语言(如Python),你可能会觉得编程是一种综合的体验:

它们具有表达性、可读性、简洁性、精确性和可执行性。这意味着我们可以排除中介语言,使用一种语言来探索、学习、教学和思考。

于是编程成为一种体验一致的活动。你的探索、学习、教学和思考都能在一门语言中进行,不再需要在自然语言、流程图、数学符号、编程语言中来回切换。

一个例子

我们跟着作者来看一个例子: 广度优先搜索(BFS)算法(该算法常出现在各类教科书里)的伪代码表示和Python语言表示。

维基百科解释说:

广度优先搜索是从根节点开始,沿着树的宽度遍历树的节点。如果所有节点均被访问,则算法中止。

先看看伪代码表示。

伪代码(英语:pseudocode),是高层次描述算法的一种方法。它不是一种现实存在的编程语言;它可能综合使用多种编程语言的语法、保留字,甚至会用到自然语言。

上述伪代码用了不少逻辑学常用的符号,如果你没接触过伪代码,它们可能看起来怪吓人,有点像数学证明。

接着让我们看看它的Python表示:

它比伪代码短几行,而且因为它使用的单词比符号多,所以我认为它更易于阅读。此外,与伪代码不同,我们可以运行它、显示结果并调试它。

人们常认为伪代码的可读性优于编程语言,但这儿作者有个很有趣的论点:Python使用的单词比符号多,所以我认为它更易于阅读。人们为褒奖Python的可读性,有时会说Python是可以运行的伪代码,但在Allen Downey看来这句话其实是在褒奖伪代码的易读性。

除了有极佳的可读性,更重要的是:

可以运行它、显示结果并调试它.

如果你使用Jupyter的话,上述的这些过程会更加统一。这是一种绝佳的进行教育与交流的方式。将你在Jupyter中的探索过程用ipynb分发出去,它就是绝佳的教学材料、可复现的论文。其中记录了你思考、探索、编程、调试的全景。

编程的重要目的当然就是为了能跑通代码,但它还有更多的意义。那就是通过执行代码的能力使编程成为思考和探索的工具。当我们以程序的形式表达想法时,我们使它们成为可测试验证的;当我们调试程序时,我们也在调试我们的大脑。

好的编程语言应该能够容易表述你大脑中思想的图景。由于它是可执行的,于是你能即时验证想法,你的想法容易被证实或证伪。实际上,你在执行和调试代码的时候,也是在调试大脑里的想法,想想REPL!在这个意义上,计算机语言会根本地改变我们思考的方式。

语言会影响你的思考。如果你是个笨蛋,无论你使用什么语言大约都无法说出什么妙论,但如果你是个普通人,高级语言能让你的思考更轻松些,而不必来回切换,尤其当你是新手时,这会更少地消耗你的耐心,更不容易让你在一开始就放弃。对于初学者而言,不放弃比多学一些知识重要得多。

关于语言和思考的关系,维特根斯坦做了最为精辟的阐述,《逻辑哲学论》和《哲学研究》都极为精彩。当然这些梯子你最好用完就丢:)

如果我和维特根斯坦一样激进,我可能会说,语言会极大影响甚至决定你的思考方式和深度。维特根斯坦说, 当我在用笔写作时,我的脑子并不知道发生了什么。

Allen Downey接下来论述Python之类的高级语言适合学习和教学。他以亲身例子来说明。Allen Downey写了很多书,而且他把很多书都放在nbviewer AllenDowney,他最近写了一本关于数字信号处理(DSP)的书([Think DSP](Think DSP))。使用Python编写了一个简单的库(thinkdsp),且使用Jupyter写代码教程,之后分发给学生。

我同时编写代码来检验我自己的理解,同时把这些理解都解释给我的学生们听。学生们也可以来运行代码来去开发一个心智模型,进行修改以测试他们自己的预测,为他们的项目扩展我的代码。
大多数教科书和课堂都靠学生们主要使用纸和铅笔来工作并使用数学这门工具来传授信号处理。使用这种方法,唯一的选项是“自底而上”,从学习复数算术这个并不是最热点的专题开始,并花费数周的时间和许多页纸的篇幅才能达得相关的应用程序这个阶段。
用一种计算方法,我们可以“自顶向下”,从实现最重要算法的库开始,比如快速傅里叶变换。学生可以先使用这些算法,然后学习它们是如何工作的。他们可以看到最重要的想法,比如光谱分解,而不会被细节所蒙蔽。他们可以在真正的应用程序上工作,在第一天,提供动机去更深入。他们可以有更多的乐趣。

这段极为精彩。大多的教程和教师关心的始终是如何灌输知识,如何教会学生语法、算法、当然还有解题法,他们完全不考虑学习者的热情、兴趣和困惑,就这点来说,我认为教师这个职业大部分可以轻易被AI取代。

我很赞同Allen Downey直接提供可运行的算法库的做法,学生一开始能把它运行起来,他就不大可能在遭遇自底而上的一堆细节时放弃,而且也可能在探索和修修改改中产生兴趣。Philip GuoWhy Python is a great language for teaching beginners in introductory programming classes开头里说:

我坚信大多数入门学习者根本不关心计算机科学或编程语言理论,而只是想让计算机简单运行一下他们的代码,而不必大费周章写一堆样板代码。需要编写的代码越少,可能遇到的bug和错误就越少; 而遇到的bug和错误越少,他们就越不容易在入门之初因为沮丧而放弃编程。

Allen Downey的这段话特别打动我:

他们可以在真正的应用程序上工作,在第一天,提供动机去更深入。他们可以有更多的乐趣。

当你在脑子里把读者想象为一群有情感、追寻兴趣、遇到挫折就可能放弃的人类的时候,写出的必然是Allen Downey写出的这类教材。这类教材我想到的还有head first系列

我一直坚信大多数写教材的人完全不记得自己当学生的样子,更糟糕的是他们还缺乏共情能力,否则我们无法理解为何满世界会是那些令人没有胃口的知识罗列风格的书,它一定是写给那些已经懂了的人,用作考试复习提纲。

Allen Downey在结尾处说:

现代编程语言在性质上不同于它们的前辈,我们也只是刚刚开始认识到这种差异的含义。

人们大多时候只把语言看作一种传递和表述思想的工具,认为一个人的思想在他脑子里一次成型,之后只是用某个媒介展示出来。但复杂的思想往往不是一蹴而就的,它可能要经过思考、检验、校正、推翻等环节不断迭代,如果我们有一种好的语言,同时具备表现力、可读性、简洁性、精确性和可执行性。那么它就可能在本质上改进思考过程。

如果不以冯诺伊曼的方式看待编程(一种办事员的工作,基本不需要理智参与), 而用图灵的视角,那么它的确是 非常吸引人的

理论上你用二进制或汇编也能写出所有的程序,但如果没有现代化的高级语言,很多工作,是极为艰难甚至不可能的,毕竟大多数人的大脑应对复杂度的能力是有限的。人类是擅于利用工具的动物。这些工具延伸和改进了他们的生物有机体。

我最近在读《史蒂夫·乔布斯传》,援引他年轻时对计算机的看法作为本文结尾:

I think one of the things that really separates us from the high primates is that we’re tool builders. I read a study that measured the efficiency of locomotion for various species on the planet. The condor used the least energy to move a kilometer. And, humans came in with a rather unimpressive showing, about a third of the way down the list. It was not too proud a showing for the crown of creation. So, that didn’t look so good. But, then somebody at Scientific American had the insight to test the efficiency of locomotion for a man on a bicycle. And, a man on a bicycle, a human on a bicycle, blew the condor away, completely off the top of the charts. And that’s what a computer is to me. What a computer is to me is it’s the most remarkable tool that we’ve ever come up with, and it’s the equivalent of a bicycle for our minds.

参考