Versions:2015-07-04 2015-07-06 2015-07-17 2015-08-10 2015-09-16 2015-10-13 2015-11-03 2016-10-01 2017-05-06 2017-10-29 2017-11-29 2017-12-16 2018-04-01 2019-11-18 2020-02-17 2024-04-16

数学和编程

好些人来信问我,要成为一个好的程序员,数学基础要达到什么样的程度?十八年前,当我成为大学计算机系新生的时候,也为同样的问题所困扰。面对学数学,物理等学科的同学,我感到自卑。经常有人说那些专业的知识更加精华一些,难度更高一些,那些专业的人毕业之后如果做编程工作,水平其实比计算机系毕业的还要高。直到几年前深入研究程序语言之后,对这个问题我才得到了答案和解脱。由于好多编程新手遇到同样的困扰,所以我想在这里把这个问题详细的阐述一下。

数学并不是计算机科学的基础

很多人都盲目的认为,计算机科学是数学的一个分支,数学是计算机科学的基础,数学是更加博大精深的科学。这些人以为只要学会了数学,编程的事情全都不在话下,然而事实却并非如此。

事实其实是这样的:

数学是异常糟糕的语言

这并不是危言耸听。如果你深入研究过程序语言的理论,就会发现其实数学家们使用的那些符号,只不过是一种非常糟糕的程序语言。数学的理论很多是有用的,然而数学家门用于描述这些理论所用的语言,却是纷繁复杂,缺乏一致性,可组合性(composability),简单性,可用性。这也就是为什么大部分人看到数学就头痛。这不是他们不够聪明,而是数学语言的“设计”有问题。人们学习数学的时候,其实只有少部分时间在思考它的精髓,而大部分时间是在折腾它的语法。

举一个非常简单的例子。如果你说cos2θ表示(cos θ)2,那么理所当然,cos-1θ就应该表示1/(cos θ)了?可它偏偏不是!别被数学老师们的教条和借口欺骗啦,他们总是告诉你:“你应该记住这些!” 可是你想过吗:“凭什么?” cos2θ表示(cos θ)2,而cos-1θ,明明是一模一样的形式,表示的却是arccos θ。一个是求幂,一个是调用反函数,风马不及,却写成一个样子。这样的语言设计混淆不堪,却喜欢以“约定俗成”作为借口。

如果你再多看一些数学书,就会发现这只是数学语言几百年累积下来的糟粕的冰山一角。数学书里尽是各种上标下标,带括号的上标下标,x,y,z,a,b,c,f,g,h,各种扭来扭去的希腊字母,希伯来字母…… 斜体,黑体,花体,双影体,……用不同的字体来表示不同的“类型”。很多符号的含义,在不同的子领域里面都不一样。有些人上一门数学课,到最后还没明白那些符号是什么意思。

直到今天,数学家们写书仍然非常不严谨。他们常犯的一个错误是把x2这样的东西叫做“函数”(function)。其实x2根本不是一个函数,它只是一个表达式。你必须同时指明“x是参数”,加上x2,才会成为一个函数。所以正确的函数写法其实看起来像这样:f(x) = x2。或者如果你不想给它一个名字,可以借用lambda calculus的写法,写成:λx.x2

可是数学家们灰常的喜欢“约定俗成”。他们定了一些不成文的规矩是这样:凡是叫“x”的,都是函数的参数,凡是叫“y”的,都可能是一个函数…… 所以你写x2就可以表示λx.x2,而不需要显式的写出“λx”。殊不知这些约定俗成,看起来貌似可以让你少写几个字,却造成了许许多多的混淆和麻烦。比如,你在Mathematica里面可以对 x2+y 求关于x的导数,而且会得到 y'(x) + 2x 这样蹊跷的结果,因为它认为y可能是一个函数。更奇怪的是,如果你在后面多加一个a,也就是对x2+y+a求导,你会得到 2x!那么 y'(x) 到哪里去了?莫名其妙……

相对而言,程序语言就严谨很多,所有的程序语言都要求你必须指出函数的参数叫什么名字。像x2这样的东西,在程序语言里面不是一个函数(function),而只是一个表达式(expression)。即使 JavaScript 这样毛病众多的语言都是这样。比如,你必须写:

function (x) { return x * x }