如何学习编程语言

(零)写在前面

这是我在2012年的时候在豆瓣的日记以及帖子的合集,后面在曾经的42区上托管过一段时间,在42区沦陷后,整理成了Markdown文档放在了GitHub上,今天再一次整理,作为合集放在我的wiki里面。

五年前的我可能不会想到,其实我现在也能沦落为一个ED,做着我当年嘲笑自己的事情:好像从个人发展的角度看,我反而退步了。但是现在再一次看看当年一时冲动写下的这几千字,好像自己还是能够学到些什么。

(一)一个小时学会C++?

2012-10-10

某天上课的时候听到一个同学说,“C++其实只要一个小时就学会了”。

当时我没说什么,只是脸上的表情有点不自然。

那个讲课的老师是个ED(Enterprise Developer),那同学的发展方向也像是一个ED。而他这句话,令我表情不自然的原因貌似就是关于语言学习的问题。

学习一个语言究竟要多久?究竟该如何学习一门编程语言?


Haskell Wiki上有一篇关于Haskell入门介绍,貌似叫做“十分钟学会Haskell”,可是那篇文章费了我很久的时间也没有能够看懂多个大概。然后等到在学校图书馆找到一本《Real World Haskell》后,花了三天时间没有写出一行代码,单只是在那里理解静态多态类型是什么,然后后来遇到模式匹配也卡壳很久,到了Monad就已经决定放弃了,转去回头看Scheme了。

或者是我理解东西理解起来有点慢吧。

可是,一个小时,对于C++这么庞大而复杂的语言来说,真的不知道是什么概念。


我带过新手,初入门的时候,大概用了一个月的时间,学习C语言,他能够理解到了基本控制结构和数组。然后就看他就不太怎么继续的下去了。而即便是接触过其他命令式编程语言甚至是C语言的老手,接触C++然后并学会的话,也不知要多久:至少从非花括号系的语言转换过来适应语法就应该要不止适应一个小时吧,从C语言过来也要理解C++加入的那么多非OO相关的新元素(常类型、引用语义等等,这些概念不是一般的耗时)。

嗯,好吧,你不是新手,你也用过花括号语言,你了解OO,或者直接说你就是一个无敌了的Java或者C#的高阶ED而且还有C语言编程基础的那种。但如果你是新入门C++的话都很难保证一个小时学的完。(好吧,我可能会把ED的重视工程应用而忽略高阶应用的特长给忽略掉,那样的话他只要对比一下某些东西有什么不同就可以把Java/C#的习惯移植过来了。)除非能够在短时间内了解C++强大的符号体系和运算符重载的威力,当然,不要忘了模板。    你也许会说,那又怎么样?都不在话下的:这些放到我脑袋里面就跟玩儿似的全部都给保存下来了,自带扫描仪和OCR系统外加高容量高速度存储的人你比得过吗?

好吧,那么,试问下,泛化类型你需要理解多久?traits技法你需要理解多久?编译期生成/运行代码你需要理解多久?如果说你在一个小时之内搞得定,OK,你赢了。

(二)编程语言的选择

2012-11-01

如何学习编程语言?

这个问题回答起来可不是一般的困难。


每个人的情况都是不一样的:理解能力、认识水平或者是其他方面的偏好及差异;这样子就会有无数种情况,根本不可能完全涵盖。但,其中的某些东西是统一的:无论是什么阶段什么基础或者是要准备学习什么语言,其大致的步骤总是有那么些个。


那我们试着梳理一下:

先决条件

首先,要确定学习哪一个编程语言。

这之中的学问可不浅~有多少人纠结在了这个地方,学了多少内容,而至今还是只理解皮毛(好吧,其实我就是一个活生生的例子)。确定一个准确的目标很重要,当然,确定这个目标的标准之一就是你准备学了它干什么。

好吧,我不去扯那些社区人经常提到的关于入门用什么语言的问题了,国人也历经了很多代的入门程序设计语言了(Basic、Pascal、C),至今身边的同学仍在抱怨,无论学了什么东西都觉得根本都没用,学了也白学。

所以,其实没必要觉得入门难,如果你认为很难,那可能是没有找到你真正适合的语言。而同样的,即便你是一个身经百战的高水平Coder,也不要太小觑了任何一门编程语言:总有些东西令你费解让你觉得不爽的。

如何选择

那究竟该怎么选呢?

如果你只是想学习程序设计,来进行研究着玩(好像这样蛋疼的人不多),那选择一些小型的,可研究性强一些的,更能够贴近编程本质和有利于问题抽象的语言:Lisp/scheme是一个非常不错的选择(当然跟着该推荐的是SICP或者HTDP),当然可能利用一些第三方库或者某些实现也同样能够实现一些实用性程序应用,可(至少目前)这方面不是它所擅长,不比较推荐。

而如果想真正写一些实用性的东西,甚至直接可以拿来做大项目或者工程,或许目前应用比较广泛的C系语言(C/C++/Java/C#)是不错的选择,但这并不是仅有的选择:Python是一个很好的语言,拥有众多优良的继承自C或者是自身实现的库,既可用于做大型项目/工程,又能够做一些简单方便的小工具,简单优雅的语法注定了其易于学习和使用的特点;Ruby是另一个推荐的选择,较之于Python在某些方面的特性又更好出许多,直接继承自Lisp和Smalltalk的设计思想和及强大的元编程模式让代码写起来又是另外一种愉悦的享受。

Hackibility

Hackibility是Geek们的追求,而也有不少编程语言给了Geek们折腾(折腾与学术研究的区别就不用在这里解释了吧)的机会。元编程是一个能吸引他们的好东西,而自由的语法模式和丰富的应用模式也会是不错的噱头。Ruby前面已经提到过了;C++也很适合该需求,继承自C的基础语法和模板元编程的扩展以及C++11标准中又加入的更多值得折腾的特性;CommonJS标准也使得Javascript的hackibility不断提升,Node.js平台的应用也越来越多。

原则

总之,大致能够理出以下几点原则:

(三)端正态度

2012-12-30

确定了语言之后,不是说立马就可以行动了。

端正下某些态度也是十分必要的。   

首先,要尊重自己的选择。

没有一个编程语言是完美的,多少都会有一些不足之处,但是当你根据需求做出了选择之后,就要尽全力去爱上它,不会因为喜新厌旧或者是其他的原因而放弃或者是讨厌最初做出的决定。不要今天看到某个不错的特性然后就转而去搞其他的,结果回头来再看怎么都不喜欢原本的那个了。

不过也有折中的选择:选一个Hackability较强的语言,比如C++,自扩展性决定了它可以实现各种神奇的语言特性:泛型编程/函数式编程/元编程,甚至很多高级的库也完全可以由自扩展来实现。所以,想要折腾,一个附带了boost系列库的C++绝对经得起。   

其次,不要把自己锁死在一个语言上。

虽然说是要尊重自己的选择,可是如果是愚忠,视野范围仅限于首选的语言上,那就更不是一个好的做法。多了解一些东西能使你更好的更有效的利用和调度资源。很多大神们都多多少少会写一些脚本(Perl,后来是Python/Ruby),而这样子则能更好的简化其工作——某些时候脚本快很多,无论是写起来还是运行起来。

但也有例外,除非你是天生的专一的Perl程序员(这个情况好少),或者是专一的Python程序员。但一般这样子的话所要精通的就不只是一两门脚本语言之说了。

另外,要尊重别人的选择。

正如上面所说,没有一个编程语言是完美的,所以语言之间没有绝对的优劣,即便是相对,也要在参考条件很占优势的情况下才能分清优劣。

所以,即便是个人偏好问题,也不要去对比优劣性。没有差劲的语言,只有差劲的程序设计。

(四)如何学习编程语言

2013-01-06

入门一个编程语言,肯定是从语法结构开始的。

各种各样的标识符和字面量的构成、支持的数据类型、对应类型的各种操作和如何添加自定义结构(类型、函数、宏等等)。如果之前有学习语言的经验的话,对比学习则会有更好的效果。而且如果是特定类型的语言,语法结构上很大程度上是相通的,类比学习更会正方向的促进学习效率的提高。

单只知道语法是不顶用的。“回字有四种写法”,知道了如何写却写不出内容来。那么,下一个需要了解的就是一个语言的core library & std library(核心库和标准库),以及各种有用的API。这样子你才可能会去通过对库的使用来做出一些实用工具。因为对基础的库的理解可以促使你形成一种思想:如何去利用已有的东西来构建自己新的东西。


那够了吗?当然不够。如果没有解决问题的思想方法,那么你还要去学习如何去组织和管理数据,和如何进行算法设计。没有要求去形式化的学习数据结构/算法分析,但至少要知道分析和解决问题的思想,并且能够有效运用。不要让程序成为一团乱麻,跟流水帐似的一条条语句罗列下来的样子。

嗯,还不够。因为就算能够解决问题了,还是不足以让你形成一个系统化的思维模式。要想更好一点,拿一个该语言擅长的领域的完整程序来,分析一下他的源代码,顺便自己再模仿实现一个。从架构成面上如果能够设计出一个完整的应用程序来,则是这一项训练的最高目标。


好吧,如果你想停止,那么可以了,至少你可以依照这这项能力找一个不错的码农工作了。但如果你还想继续,还觉得没有虐够的话,去读一下标注库/核心库的实现吧:能够被语言采纳为库的代码往往都是优秀的,无论是风格还是效率还是解决核心问题的处理方法。那么,学习库的实现则更是一个提升层次的方式:当然,如果你想,也可以自己试着扩展一下标准库。

没,还没完呢。

至少还有很多东西给忘了呢。

语言总会有不足之处,也总会有过人之处。但是,如果当你好奇,想了解一下该语言的特性如何实现的,或者是想通过对底层代码的修改来扩展该语言,那么,下一个要做的就是去读语言的源代码吧,或者是去实现一个吧。

当然,这是对于那些自扩展性不强的语言来说的。否则,添加语言特性这东西完全可以通过语言本身实现(如C++、Ruby、Lisp等),总之,对语言的hack绝对是受欢迎的。而且,什么时候送给官方一个patch的话,那你就真正为一个大神了。

(五)编程范式

2013-01-14

正如树立正确的态度一样,对于问题分析和方法使用的意识也是很重要的,而决定这些问题的,则很大程度上在于对模式和范式的理解。


先说范式(Paradigm)。

范式是指编程中普遍的基础风格/形式:如面向对象、过程式、结构化、函数式等范式。范式是确定程序员的编程世界观方法论的关键,它决定了你如何认识程序中的基本单位以及如何去对待他们。命令式编程中,一切都要以语句作为基本元;过程式编程中,以过程/函数为基本元;面向对象中一切都是对象(理想状态下);函数式则是一切都是表达式;Lisp的基本单位是列表(list);汇编则是一条条的助记指令。

但范式并没那么简单,就跟辩证唯物主义和科学发展观一样,没那么简单。


首先,对范式要有一个概要性的了解。特别是对于多范式语言,多少要能够写出那么一两个对应的示例代码来,而且能对其特点进行分析。

然后就是要用对范式。同语言类似,范式本没有优劣对错,但解决同一个问题,总会有那么一个更优方案。十几行代码就能解决的问题,没必要去话几十分钟画一幅UML图来构建一个完善的对象系统。ED的习惯尽量不要去养成,能用更优的方法,请尝试,否则只可能增加问题的复杂度。

再就是要看清本质:试着去分析一个范式其根本、其实现方式及其目的。很多范式,如事件驱动(data-driven)、泛型(Generic)等范式,本身并非语言特性,而是库或者是语言实现对其进行的扩展。如果你能将一种范式应用/移植到一个语言上,并会比较明显的增进效率,则类似问题的分析和解决方案肯定不在话下了。

(六)设计模式

2013-03-19

设计模式的创造者和发现者并不是GoF(Gang of Four,《设计模式》的作者),他们只是把这些东西系统化了一下,给整合在了一起,命了个名字而已。

没错,能把他们系统化起来,再归结到一本书里面,已经很牛了,更不用提《设计模式》对几代程序员/工程师的影响了。

可是,任何事物都是有两面的,模式的出现是在很大程度上提升了项目的开发效率和软件的工程化水平,使得程序设计方法有了依据可循。但同时带来的问题是对于模式的滥用:为了设计模式而设计模式的过度设计比比皆是,开发效率因而随之下降,并且引入模式之后增加了问题及程序的复杂度,使得扩展/优化/修改变得会非常困难。

这并不夸张,ED们不经常出现这种情况么?


那么,要避开这些问题,就要首先正视模式。

软件设计中最重要的演变就是抽象化程度的不断提高:把共同的指令流抽象化,就变成了过程;把相关的数据抽象结合在一起,就变成了抽象的数据类型/数据结构;再进一步深度抽象:提取出抽象数据类型的共同部分,就产生了继承/封装的面向对象(OO)。

设计模式是更高一级的软件抽象:对于OO的构建方式的深度抽象。但是,如同在没有模式之前出现的各种方法仍然只是新方法一样,模式也并不是必须的:在GoF出书之前仍然可以解决软件设计所面对的所有问题。你不必知道什么是Singleton、什么是Provider、什么是Factory,你所做的本来就是发现问题、分析问题、然后解决问题,然后继续发现问题,或者是使用分治法来一步步解决大型问题。

但不久之后,软件开始工程化了,变得复杂了,程序员之间的协作要求变高了,需要统一的规范和协定了;不然面对鱼龙混杂的Code和Coder,只可能更多的时间都在读代码而不是写代码。架构师的出现是解决这个问题的一个很好的方法,另外一个方法就是模式了。模式提供的是规范之外的一个基于代码层面懂得约束和协定:当两个不同的开发者对待同样一段代码时,都会对它进行同样的解释和使用,对待同样一个问题的时候,都会提供一个相似的解决方案。所以,这种约束既同步了不同程序员之间的解释,又能够让程序员对代码的使用和构造大致符合一个公共协定,而只需要了解这个公共协定就能够直接使用该段代码的接口并无需了解其实现,代码的复用性大增。


但回过头来看,如果你只是在写一个小东西,或者是几个人完成一个比较小的项目,或者是在编写你所负责的模块的内部代码(不许要对其接口开放),如果在这之中只要一个控制全局状态的对象,那么,一个注释明确见名知义又好记又好用的全局变量完全能够代替Singleton模式。因为,这并不需要约束,你和同伴都知道他是什么,而且简单的方法简单实现,反而会易于理解,并且还会避免引入模式后的复杂度。一行代码和六个模式结合都可以实现输出“Hello, world!”,你更喜欢哪一个?

模式并不是必须的,特别是在引入后会绕更多弯的情况下。

如果可以,请用最直接的方法。

(七)我想用电脑煮咖啡

2013-04-02


题外废话

写到上一篇“设计模式”就卡壳了,然后开始思考该怎么继续下去。我的算法设计与分析很弱,所以不敢拿出来显摆,而且这也归到了程序设计方法里面去了。不过后来发现,很多人想知道究竟编程能拿来做什么,这个确实是我遗漏掉的一个问题。这是整理了我在豆瓣C语言小组的一系列回帖,分享一下希望能够有帮助吧。

原帖地址:http://www.douban.com/group/topic/37223604/


豆瓣Emacs小组的简介里有一句话说,“我想给电脑装上脚踏板”,其实用一种夸大的口气说Emacs Lisp的强大,可API接口的丰富,能够让他们做很多事情。

但是无论怎么学习编程语言,总感觉没什么用。总是翻来覆去的老是在学习语言,看别人的代码,抄别人的代码,默写别人的代码,实现别人实现过的东西,那我们学了他有什么用呢?

我还在Python小组活跃的时候有人喷过Python,说现在无论什么都是拿来做Web(网站开发)的,然后就很愤慨,可是愤慨过后,就没有音讯了。

那么到底学习编程语言到底能做什么呢?


开发网站和游戏这种庸俗的事情就不必说了。

任何机器能够做的事情都能做, 比如用电脑煮咖啡,让它帮你制定明天的出行计划,寻找出每一个多年未见的故交,以及以后你可能会想象出来的各种新花样儿。。

煮咖啡

通过传感器和一系列的PLC/MCU等等对咖啡壶的状态和行为进行控制和分析,然后通过分析得出的结果进行自动化的给出相应的执行命令(当然,这整个过程中起到非常重要的作用的就是相应算法的编程实现),如果你想更好的话,可以在咖啡壶上装上轮子,让它煮好直接跑到你面前来。这个涉及到一些硬件级的问题,感兴趣可以,实现起来需要很多人辅助才行。

寻找好友

寻找好友不用说嘛,使用过腾讯的QQ圈子的都能懂得。因为他能够掌握得了每个人不同的好友资料然后进行统计分析和数据挖掘,找出其中的相关性,来推荐给你。也许我们获取他们的好友列表什么的不容易,但是可以从朋友网,人人网,微博等入手,抓取并分析他们的状态、关注和粉丝列表,然后一步步深入,同样利用数据挖掘技术得到的结果都很令人吃惊的。

行程安排

行程安排要比前两个更实用一点,而且相对来说是越用越智能的系统:你最初需要做的只是用它来进行一些个人信息的管理:比如整理每天要做的任务、制定下个月的理财计划、添加/查询常用联系人的信息和收发电子邮件等等,但是当它得到的信息很多的时候,会慢慢的理解你的习惯和模式,然后进行一些推荐的活动安排,并帮你妥当完成处理一些琐事,而你所做的只是等到活动时间到了跟大家一起High就可以了。(如果嫌自己添加信息麻烦的话,还可以将你的之前的手记等信息通过OCR或者其他的技术导入,然后让他自动分析。)

如何实现呢?