自学编程,从此开始

上第2学堂,听有趣的编程课

课文: 《编程学习的特定困难》 (点击查看完整内容:视频+评测+讨论+……)

作者:第2学堂

编程学习学什么?怎么学?参加培训?还是自学好?怎样保持学习兴趣和信心,避免从入门到放弃? 为什么有些人开始学习进展很快很大,却又很快陷入瓶颈期?

课文题图

 

编程学习的特定困难

1. 从“编程”是什么,看你应该学什么

想要回答学编程学什么,似乎得回答:“编程”是什么?

编程?不就是编写程序吗?那用什么语言写?写出来的什么内容算是程序?什么不能算是程序?

写程序,当然用“计算机编程语言”写啦!什么汇编语言、C语言、Java语言……用计算机编程语言写的东西才叫程序嘛——打住。幸好我们看了视频,视频里提到三位祖师,其中年纪最大的,也是唯一的女性,叫“阿达”。

阿达,被称为“世界上第一位程序员”。全名“阿达·洛芙莱斯”,英国人,出生于1815年,在中国,这是大清嘉庆年间。

阿达的父亲,英国大诗人拜伦。阿达的母亲,一位数学研究爱好者。在阿达才一个月大时,父母就离异了。你应该猜得到在双方分居协议里,阿达跟了谁。为了避免出现像父亲一样的 “危险的诗人倾向”,母亲一直鼓励阿达研习数学。

1834年,阿达的一个数学家兼机械工程师的朋友,发明一台“分析机”,可用于数学计算。阿达致力为该分析机编写算法,并于1843 年公布一套使用该机器进行伯努利数运算方式,被认为是世界上第一个计算机程序。

1843年写一段算法,用的是什么计算机语言写的?都不是。因为那时还没有计算机编程语言。所以阿达用的英语,人类的自然语言 ,最多加一些数学符号,描述的算法。问题来了,在阿达之前研究数学的人很多,比如她的妈,肯定也使用过英语记录过某些解题算法,为什么妈妈即写的算法不是计算机程序,阿达写的却是?

要回答这个问题,首先得对“计算机程序”做出限定。其实也很简单:你写的算法能被计算机执行,就是一段程序,不能被程序执行,就不算计算机程序。阿达妈妈写的算法再如何条理清晰,因为只是写给人类看的,和计算机无关,所以不算程序。

等等!阿达用英语,用人类自然语言写的算法,居然有计算机可以执行?那岂不意味着在嘉庆年间,就已经有了自然语言识别的人工智能计算机存在?谷歌、微软、腾讯、微软等一票现代公司岂不羞红了脸?

不是啦!那阿达为之写程序的计算机,长这样子:

全是传动杆、齿轮、拔片什么的,没错,这是一台机械计算机,而不是我们现在所用的电子计算机。它执行指令的方法,就是找个人,看一句阿达写的算法,然后在这台机器上执行一步或多步。真相大白:原来机械计算机(至少阿达所使用的这台)必须依赖人工操作,所以阿达写的程序当然使用人类的自然语言表达。

不管是电子计算机还是机械计算机,每一台计算机都有一套最小可执行的操作步骤,而你为了解决某个相对复杂的问题,必须以某种方法这些步骤组合起来,再交给计算机执行,那么,对这套步骤组合的描述,就是一段程序。

就让我来凭空“制作”一种机器,这种机器可以执行三个最小步骤,分别是:A、关上冰箱门,B、打开冰箱门,C、把大象塞进冰箱。现在,请你写一段“如何将大象装进冰箱”的程序。只见你快速写下: “先B,再A、最后C”。掌声四起,恭喜,你是一名合格的程序员了!

冷静一下,上面的例子中,使用用“A”,"B","C"对应那台神奇的机器可执行三个最小步骤,这是在用“文字”对应现实内容,因此“A”,"B","C"就是这台机器的编程语言。而你所写的内容中用到了“先……再……最后……”,用语简练、清晰、体现了你一流的表达能力。最后,你在表达什么?你在表达如何将大象装进冰箱,你非常清楚应该先开门,再塞大象,然后关门!天啊!你解**决问题的方法,如此精确、简捷……莫非,你就是传说中的大象装箱问题解决专家?

使用编程语言表达解决问题的方法,就叫编程。将表达内容写出来的内容,就叫程序代码。

阿达·洛芙莱斯会成为人类的第一个程序员,看来并非偶然,事实上她大有优势:

  1. 编程语言:阿达是在为机械计算机写程序,使用自然语言;
  2. 解决问题的方法:早先,计算机就是主要用来做数学计算。阿达有一个很爱数学研究的老妈,并且从一直鼓励她研究数学;
  3. 表达: 阿达有一个世界著名的大诗人老爸,所以我们相信,哪怕是在表达方面,她应该也有比普通人好很多的优秀基因。

当然,就算此三点既备,阿达仍然无法成为人类的第一个程序员。因为她还需要一台计算机。感谢她的那位数学家兼机械工程师的朋友:查尔斯·巴贝奇

所以,一盘冷水从头浇下,对不起啊,你还不是一名程序员。因为能执行把大象装进冰箱的指令的机器,我还没制作出来,当然,其实也快了,毕竟另外两步,我觉得很easy的事。

不开玩笑,说正事。想成为一名程序员,你至少得学:

  1. 至少学习一门编程语言;
  2. 解决问题的方法(以下有时也简称为“算法”);
  3. 良好的(使用编程语言)的表达能力;

其中最简单的,当然就是第1件事:学习一门编程语言。任何一个告诉你学编程就只是“学一门编程语言了事”的“老师”,不是骗子,就是傻子。

其中第二点比较令人疑惑。“解决问题的方法”?学什么?怎么学?

一百年前的阿达,面对一百年前那台全是机械组成的计算机器,芳唇轻启,说过这么一句话:“未来,这个机器可以用来排版、编曲或是各种更复杂的用途”。阿达很不幸只活了36岁;但仅为这一句话,那就是我们所有程序员心中永远的敬仰。当今的计算机,早就可以排版、早就可以编曲;可以订餐、可以购物、可以聊天、可以炒股、可以绘图、可以游戏……然而在实现这些千变万化,看似毫不相干的业务功能的底层,有大量的基础算法,是一样的,都可以归到数学上逻辑。

老师问一年级的小明同学一个问题,小明陷了久久的沉思中,最后求老师能不能把问题再说一遍。老师说:“家里原来没有白菜,妈妈买回一斤白菜,爸爸也买回一斤白菜,问家里现在一共多少斤白菜?”小明听完直拍大腿:“我说怎么一直算不出来!刚刚我一直按萝卜算来的”。

笑话背后想说的是:剥离业务的表象,许多问题的解决办法最终可以抽象出和业务基本不相关的数学问题、逻辑问题(通常只说数学,因为逻辑问题也是数学问题)。

听说,现在的计算机程序,都可以写诗了——如果拜伦能预知计算机终有一天也可以成为诗人,会不会选择留下来陪这对母女?

2. 编程知识的变与不变

编程是“使用编程语言表达解决问题的方法”,这句话少了什么?少了主语。加上去,得到:

程序员 使用 编程语言 表达 解决问题的方法。

画成图,是:

三者看起来关系简单明了:A运用B表达C……可惜,涉及到关系的事情,从来就不会这么简单。很多事情,需要我们多问几个问题:

  1. 解决方法是什么问题的解决方法?所以解决方法和上层业务问题有关;
  2. 解决方法交给谁来执行?交给机器。所以解决方法和机器有关;
  3. 编程语言谁在用?表面上是程序员在用,但最终要翻译以后,是机器在读并执行;
  4. 程序员既得懂编程语言,又得懂业务,而后两者都和机器有关,所以……

加上层次关系以后,大概是这样的图:

这张图表达了编程这件事牵涉到的三个层面。编程夹在中间,往上看,是业务和人,往下看,是机器。因此,关于什么是“编程”,我们有了一个纵向新解读:

程序员 想办法 让底层的机器 来解决 上层的 人或业务的需求。

其中,上层的“人”或“业务”的需求,是千变万化的,而底层的机器,变化则比较慢。 当然,一旦机器发生变化,通常意味着一个新的“革命“到达。比如人类刚刚经历过的:智能手机的出现与广泛应用,一方面极大地激发了大量的新的人或业务的需求,一方面也引发了编程行业的许多大变革。

由于不管什么业务,最终总是服务于人,所以,也可以将”业务“层面归入“人的层面”。这时就更有趣了:原来所谓的程序员,就是夹在上面的“人”和底下的“机器”之间的劳动者。那,难道程序员不算人吗?算。作为计算机的用户时,程序员是人,但当程序员写程序向上提供服务的时,程序员不是人,他得是“神“。而神,是万能的,是无所不知的。神必须懂机器。

有人反对程序员去了解机器:理由是:人类不断改进计算机,就是为了让用户可以越发不必了解计算机底层的细节——这个理由非常正确。想想阿达摇着那台内部细节一览无余的机械计算机,跟今天的程序员敲打着键盘,确实真是天差地别。但是,这个理由一点都无法支撑起程序员可以不了解计算机底层原理的论点。因为:

  1. “细节”和“原理”不是一个概念;
  2. 如果想到阿达和我们之间隔着快两个世纪了……就会明折敲键盘和摇杆之间的差别似乎也不大;
  3. 当前计算机编程的技术发展,远未到达“脱离底层原理的理解,对上层表达没有影响"的水平;
  4. 程序员之间有竞争,懂底层的人,总是占到大优势。

程序员头顶天(人、业务),脚踩地(机器),越往地里的东西,变化越慢;越往上的东西,变化越快。所以视频里说了,我们推荐的学习方式:先学一点地底的东西,作为根基,再往上学,学着学着,感觉已有根基不稳时,就因需而学,再往下扎根学一些基础。关于根基的教与学,我们的体会是:

3. 我的案例

结合我自己的经历,再聊一聊编程知识的变与不变,以及我们应该培养什么样的知识体系。大家可以将一节当成本课的课外读物。

大学时,我是《编译原理》课科代表。不知为什么当年我对这门课很有兴趣,花了很多时间,使用课程所学的多种方式实现一个可以执行多行简单脚本语言的解释器(编译原理中的很多知识,同样适用于解释器)。结业考试时我得了78分,但已经是班级第一了。但这门课让我记忆深刻的,却是告别课上,任课老师说的一段话。她说:“我国现在还没有自己的编译器,看起来再过20年也不会有,但我还是很希望在你们中间将来有人有机会去写编译器,并且是因为产业的需求要写,因为那样才能养家糊口。虽然很困难,但我还是希望有。”

老师会有一届又一届的学生,或许其他学生能实现她的希望;就我个人而言,毕业后开始找工作,然后迅速发现所学的知识好像很不符合企业所需,于是开始迎合“有个工作”的需求,拼命地自学各种“有用”的东西。我都学了些什么呢?

  1. 先是DOS下手终端程序驻留技术( TSR )、DOS下的点阵汉字显示功能,DOS下程序内存磁盘交换技术。第一个用以弥补DOS不支持多任务的问题,第二个用以弥补当时DOS对汉字显示不友好的问题,第三个用以弥补比尔·盖茨说的“640K内存够了”这话带来的坑。勉勉强强学会这三样技术……再见了DOS,Windows开始中国普及了;
  2. Windows ? 好,我猛啃MSDN上的数千个Windows API(编程接口),用它写过工控软件上位机的界面,写过 Office套件,写过多媒体教育软件……整整十年,无数个编程学习不眠之夜;然后迎来编程生涯的下一个十年,这下一个十年,我在工作中根本没用过一个Windows的编程接口,事实上我根本不在Windows下编程了;
  3. 也曾经在工作中学习和使用过 OWL、MFC、VCL、wxWidgets、Qt 等桌面图形界面(GUI)库……但其实,随着B/S 软件结构,整个电脑桌面GUI编程,从十五年前开始就开始萎缩;再至更后面移动互联网兴起,更为加剧;
  4. 说到移动应用,那也是城头变幻大王旗。就我个人经历地的移动软件开发平台,就有: ARM Linux (C语言加汇编)、Windows CE (C++)、 .NET CF(C#)、J2ME(Java)、诺基亚Symbian(C语言),以及当前流行的Andorid(Java)和iOS(Object-C);
  5. 最后一个要提的,当然是编程语言,因为这是一个程序员最基础的武器——不仅用来写程序,也用来装逼——如果不能轻松使用一打以上的编程语言输出“Hello world”,在这个行业真是抬不起头。我就不列了。我学过的接近两打,实际工作中用过的接近一打。

庄子说 “吾生也有涯,而知也无涯,以有涯随无涯,殆已!”。想着自打入行以来,我和我的很多同行,都是白天上班,晚上学习,熬夜甚至通宵是惯有的事。有诗云:

“惯于长夜编程时,写完 Python改写 C。梦里依稀调 BUG,城头变幻技术旗。忍闻朋辈又猝死,怒饮咖啡写周记。写罢低眉蓝屏处,月光如水照QWE。”

一入编程行,永远学不完。IT行业就是一个长期知识大爆炸状态。我因为入行20多年了,所以倒也是能偶尔透过纷纷扰扰的现象,隐约看到技术变化背后的某种脉络——但请坚定相信一句话:编程行业没有上帝。编程的技术路线变迁、发展,充满各种随机因素:大企业之间的斗争、某些偶然事件,某些牛人的喜好等等。就以我经历的20年,在技术发展总体是向上的大趋势下,很多技术点在很多时候发生过歪掉甚至倒退、畸形的变异。

还好,也有些知识相对长远有效。比如说我在大学考的那78分。2006年(好像挺凑巧就是20年)我终于在工作中用上编译原理课的知识。以它为关键技术做了一套产品。没过几年有人拉我独立出一家新公司,从原公司花200万买下产权的软件产品,就是它。如果仅仅是这样一个故事,就只是一个不值一说的偶然事件。真正有启发作用的是,当我重翻大学教科书时,我才发现20年前在书里所写的词法分析、递归下降、查表法、状态机等等许多知识点,基本上都曾在我的编程生涯中发挥过作用,更别说在我学习任何一门语言时,编译原理中的大量知识点都在帮助我。因为编译原理近乎就是一门教你如何实现编程语言的课。人说“不生儿女,不懂父母”,我要说的是“不知编译,不懂语言”。注意,是“懂”或“不懂”,而不是“会”或“不会”,甚至不是“厉害”或“不厉害”。父母恩、编译情,懂的自然懂,不懂的自然不懂。

“有些花儿,要离得远一些,才能闻到它的香;有些知识,需时隔多年,你才能感受到它的力量”

计算机原理、编译原理、数据结构、计算机通信、操作系统原理、算法与数据结构、TCP/IP协议等等,都是这类能长远帮到你,让你的技术天花板变高的知识;但是它们都难啃,随便一个领域想吃透所需的时间精力,大概够你学会五门编程语言了。

“学编程就是学一门编程语言”这个想法肯定大错特错。真正的答案是学编程需要有一条合理的,同时适合你的学习路径。并且任何一家教育机构,包括我,都没办法为制定一条适合任何学习者基于任何学习起点及希望达成的任何学习目标的标准答案般的学习路径。我们有比较多、比较踏实、可靠的实践,就么这一句话:

“不要让编程新人走歪。”

编程学习几乎就是一件只要不退休、不转岗,就得一直持续的事。但最重要的学习,却是你正儿八经开始学习的前面一两年。我在企业内带人,在社会上培训人,在学校里调研学生,都看到一个现象:很多孩子之所以在编程这条路上走不远、走不好,过早地碰上自己的技术天花板,原因在于他们已有能力结构不对;而能力结构不对的原因,在于他们的已经具备的知识结构不对;而之所以拥有糟糕的知识结构,在于他们都曾经有过较长时间的错误的认知;而他们的这些错误认知,几乎都是在懵懵懂懂的初学阶段培养出来的。正所谓“三岁看大、七大岁看老”,这个道理套用在编程学习上,那才叫一个可怕。

我们面向新人提供编程教育,然后我们又希望培养出未来大大小小企业里的软件架构师级别的程序员。听起来矛盾,但其实思路很简单:你一开始路走正了,走对了,培养了正确的认识,积累了良好的知识基础结构,然后你告别老师,一路走下去,你可以成为一个系统架构师,就算种种原因未成,比如你转向管理岗位,那你也是一个拥有良好技术认识的管理者;但如果你走歪了,你将——

一开始走正很重要,但如果你不是新人,只是发现自己存在以上现象,并且认同我们的方法,也可以参考这边的课程自学,以实现纠偏。