这是计蒜客《咸鱼翻身——编程自学指南》的第一篇。《咸鱼翻身——编程自学指南》是一个旨在帮助有志于提高编程水平和从事开发工作,但是缺乏学习资源和指导的学习者。
更多内容,敬请关注专栏 代码视界
作者:到处挖坑蒋玉成
链接:https://zhuanlan.zhihu.com/p/23869385
来源:知乎
以下是正文你是否符合以下情况?
大学计算机相关专业,对本校提供的教学资源不满意,或者大学其他专业学生,想要从事开发相关工作的本科生有较为充足的时间,但是缺乏学习资源(这里指的“资源”并不是书本或其他学习资料,而是真正意义上的“教学资源”:学校提供的上机环境、合格的老师和助教,乃至于上进心和姿势水平都足够高的同学等)如果符合,那么这篇文章将会对你有很大的帮助我将首先给出一些基本的指导思想,然后介绍一个具体的学习方法路线图。
本文将最大限度地保证这些方法具有尽可能强的可操作性。两种不同的学习路线通过自学成为开发行业从业者,有“正向”和“反向”两种路线:反向路线——指的是绕过一系列基础知识,直接从上层应用方向,以工程为导向开始学习和实践,直接参与开发工作,然后根据实际需要逐渐补充计算机科学知识;正向路线——指的是从基础开始,按照计算机专业培养计划,系统地依次学习。一般而言,正向路线比起反向路线,相对来说更加“安全”,更加不容易出现计算机科学基础知识的短板——但是缺点也是很明显的:入门难度更大,门槛更高,时间容忍度也更低。在实践中,两种路线各有千秋,既不能互相取代,也绝非互不相容——这篇文章将以正向路线为主进行详细的说明,本专栏后续的文章将会邀请一些现役工程师来介绍反向路线的相关思想方法。正向路线的三条基本原则关于“如何自学编程”类似的话题,在很多社区都已经有了不止一次的讨论——为什么同一个问题会有不止一次的讨论呢?这当然是因为,当读者们按照前辈们给出的书单开始读书上课,之后没多久就会受到这样那样的打击,最后信心耗尽而彻底放弃。大多数前辈自身的姿势水平高,人生经验丰富,因此不可避免地会犯下这样一个错误——过高估计了读者们的学习能力和信心。
对于本文的读者,在路线的选择上,一定要做到循序渐进,从小到大,在初学阶段尽可能地挑选不容易踩坑的路线,从第一个 Hello World 开始,通过不断地获得正向反馈来提高自己的能力和信心——在后续的内容中,我们将会应用这一原则,向大家推荐合适的路线。2、确保你学到的东西都是性价比最高的内容(高性价比学习)这里的“性价比”包括两个概念,其一是知识的可迁移性,其二是知识的可使用性。所谓“可迁移性”,指的就是软件开发领域,那些共通的,放之四海皆准的内容——C 家族语言的语法,编程的熟练度、思想和方法,以及计算机科学专业知识,都是具有可迁移性的内容,而具体的某某框架的 API 用法则不是。而“可使用性”则是另一个概念——它的含义是,这部分知识能不能立竿见影地帮助你增加代码量。很多大神都提到过,一个好的程序员必须有 XX 万的代码量——这确实是对的,但是反过来却不一定成立:如果这个世界有数万种编程语言,那么你用每一种语言都写一遍 Hello World,那么你的代码量也能达到大神说的数字——但是这代表你就有对应的编程能力吗?答案显然是否定的。所谓“好的程序员必须有 XX 万的代码量”,更进一步地说,好的程序员必须要写数量和种类都足够多的程序。而就像上文中贴出的那个链接一样,大一学生刚学完 C 语言,如果想要写一个 500-1000 行代码的大作业,最后真的能完成的也只有“图书馆信息管理系统”这样的,看起来很“low”的程序——因为只使用基本的 C 语言语法和标准库,你是很难写出非常复杂的程序的。这个时候,就需要你学习更多的计算机科学知识,来拓展你可以编写的程序的广度与深度了……你必须明白,学习的目的是为了提高自己的开发能力,让自己能够写出更大更好的程序。因此,对于走正向路线的读者来说,在没有项目牵引的前提下,对于流行技术和框架的学习应该有所节制,切忌贪多嚼不烂,千万不要陷入到“用更多的语言/框架写 HelloWorld”的陷阱中。关于这条原则的应用,将与下一条原则一起进行说明。3、循序渐进地学习新知识,选择新目标(最低耦合)@姚冬 前辈在这个答案 中说过:千万别把编程看作知识,计算机科学是知识,但是编程是手艺。既然是手艺就要用学手艺的思路去学习。以动手练习为主,看书学习为辅,甚至极端点,初学的时候除了看必要的文档都可以不看书,等有了一定编程基础后再去看书,看完书立刻动手编程去验证书上的理论知识。练习一定要循序渐进,把手艺分解成几个简单的动作,反复练习直到熟练,然后再把基本动作组合起来练习直到熟练。拿C语言举例,比如从认真写好一个Hello world开始,然后去熟悉 if for while 等基本关键字,然后熟悉字符串操作,内存操作,练习查找 排序 二叉树等基本算法数据结构,然后学习一个小型Framework。。。。作者:姚冬链接:https://www.zhihu.com/question/27574436/answer/37185893来源:知乎著作权归作者所有,转载请联系作者获得授权。
事实上,这个“循序渐进”的过程,对于计算机科学知识的学习也是一样的。玩过《使命召唤:现代战争 2》的读者,应该记得雪山潜入战役中的操作——玩家控制人物,鼠标左右键分别对应人物左右手手持的冰镐,而爬山的正确操作显然是轮换点击左右键,左冰镐插在山体内,拔出右冰镐向上勾,右冰镐插进去之后再拔出左冰镐,这样周而复始,一步一步地就能爬到山顶。而如果左右冰镐都松开的话,结局显然就是掉下山崖——同样,学习路线的选择也是一样的。不管是计算机科学知识还是某个领域的技能,都有其所前置依赖的知识或者技能。一个好的学习路线应该做到,每一次前进都只迈出一步——即每一次新学习的内容都不依赖你所没有掌握的知识、每一次新的编程练习需要掌握的新知识都尽可能少。举个简单的例子——很多大神都在强调,初学者学习《数据结构与算法》这门课程的重要性,但是实际上,对于读者来说,学习数据结构与算法的意义,其实并不是因为它的重要性,而是因为它同时满足了高性价比学习和最低耦合这两条原则——在计算机科学领域,你很难再找到另一块“完美”的课程了:抽象数据类型的实现,并不依赖于具体的语言——换句话说这门课程的可迁移性极高,在任何语言中都可以使用学习这门课程之后,你就可以去各大 OJ 平台刷题了——只要有这一门课程的知识,就可以在短时间内大幅提高你的代码量,可使用性极高这门课程实际需要的前置知识和技能,只有基本的编程基础——会离散数学更好,但它并不是必须的接下来,我将结合这三个基本法,介绍一条实际可行的,正向学习路线:一条可行的正向路线先给大家讲一个笑话:女生学编程为什么难?是思维方式不对还是学习方式不对?
编译器:「你有个错误。」
女生:「不可能!」
编译器:「你听我解释。」
女生:「我不听我不听我不听!」
编译器:「……」
女生:「你是不是不爱我了?你肯定和别的程序员好上了!」
这虽然是一则笑话,但却反映了一个现实的问题——相当一部分人,无论男女,都缺乏解决问题的思维方式。# 程序员应该具备的基本能力开发工作本质上是通过编程来解决问题,所以在开始之前,你首先要确保,你拥有最低限度的,解决问题的 Power User 的思维方式——具体地说,你要拥有以下三个方面的能力:最起码的分析能力——你不能像个文盲一样,连显示屏上明显的文字提示(很多提示原本就是中文的)都视而不见,而只会盲目地问些“我的程序运行不了怎么办”之类的毫无意义的问题——这跟上文的笑话中,女生说的“你不是不爱我了?”一样毫无意义。
一名 Power User 问出的问题,应该是“我的程序运行报错,编译器提示 error XX”这样有信息量、有价值的问题。最基本的英语阅读能力——在计算机科学的世界里,很多内容都是英文的,你至少要能够借助翻译工具读懂这些英文材料……事实上,计算机科学中使用的英文并不难,除了少数专有名词之外,其他的内容还是很浅显易懂的,这一点大家无需害怕。起码的信息检索能力——在现代社会,即使是你不打算从事计算机科学相关工作,如果你连使用搜索引擎检索关键字的能力都没有的话,那么你迟早会被这个社会所淘汰。举个实际一点的例子:在开始编程之前,你至少应该会使用 U 盘,给一台 PC 安装操作系统——因为“如何安装 Windows”这样的问题,并不依赖于计算机专业知识,而且其解决方案在互联网上满坑满谷到处都是,就算用百度都可以很容易地搜索到正确的方案。如果连这一点都做不到的话,那么你可能确实缺乏学习编程的能力……# 第一行代码:选择语言与环境「学编程应该学哪门语言?」这经常是初学者问的第一个问题——这里首先直接给出答案:使用一种较为现代的,面向对象的 C 家族语言来入门:具体来说就是选择 JAVA 或者 C++ 的一部分主要内容。为什么这里我们没有选择流行的,更容易入门的 Python 呢?这是因为在实际的开发中,C 家族语言,包括 C、C++、JAVA 和 C#,几乎占据了编程语言的半壁江山——并且,这几种语言,在语法上一脉相传,你学会了其中一个,就自动学会了另外几个的大部分内容。相比之下,Python 作为入门的第一种语言,在学习的性价比方面无疑是有劣势的——相反,当你通过 C 家族语言建立了足够强的编程基础的时候,回过头来再去熟悉 Python 就容易多了。而另一方面,如果你没有应试的需求的话,那就不要先学 C 语言——因为纯粹的 C 语言太过于古老简陋,不够现代化,会给初学者带来太多不必要的麻烦和困扰,并且应用范围极其狭窄。根据信心保护和高性价比学习这两条原则,如果没有应试的需求的话,实在没有必要在初学阶段接触纯粹的 C 语言。相比之下,JAVA 明显要现代化和友好得多——而 C++ 借助一系列标准库,在现代化程度上相比纯粹的 C 语言也有着巨大的优势,至于它的语法复杂的问题,初学阶段也可以通过只接触 C++ 的一个常用的子集来规避。必须明确一点——C++ 并不难学,因为复杂的语法是为了简单地解决复杂的问题,而不是正相反,把简单问题故弄玄虚地复杂化。
以下将会以 C++ 为例,提供一个可以参考的路线:0、 不要看《C++ Primer》作为入门书——它的定位是“程序员的第一本 C++ 教材”,而不是“初学者的编程入门书”,用《C++ Primer》作为入门书,违反了信心保护原则——你可以等到具有一定的编程能力之后再回过头来看它。1、 完成《C++ 语言程序设计基础》这门课程没有前置知识需求,较为详细地讲解了 C++ 的基本语法、流程控制、包括面向对象在内的一系列编程思想等内容——课程难度适中,对初学者非常友好。从头学完一遍,把所有的作业题都做过一轮,这个时候你就有了最基本的编程能力——如果你连课程中的实验课内容也看过一遍并动手做过一遍,那么你同样也就有了基本的、对程序进行调试的思想和能力。2、 完成《C++ 程序设计入门》(上/下)凭心而论,我个人认为这门课程的质量要比 1 中提到的要好——但是这门课程有一个严重的缺点:它要求学生已经学过 C 语言,并不适合作为第一个入门的课程。在已经完成《C++ 语言程序设计基础》的前提下,通过这门课程,你可以更多地学习到面向对象编程思想与方法,C++ 的内存模型,以及异常处理等 C++ 特有的内容。到这一步,语言的学习部分已经几乎完成了——如果有兴趣的话,你可以自己去搜索一下,关于 C 语言的一些函数(例如scanf的用法)——现在你已经可以理解它的相关内容(比如为什么scanf括号后面的变量名要加 & 符号)了。3、 在做本地练习的时候,如果读者已经有自己喜欢的编辑器 / IDE 的话,那按照自己喜欢的去配置环境就行。如果读者对此没什么概念,那么 Windows 用户请直接根据这个答案,配置使用 Visual Studio 作为开发环境, 或者使用《C++ 程序设计入门》中介绍的 Dev-Cpp 作为开发环境,macOS 用户请直接去应用商店下载 Xcode,配置方法可以参照 Visual Studio。本文不讨论任何关于编辑器 / IDE 圣战的相关内容。4、 刷题到这一步,你已经具备了最为基本的编程能力——但是这能力还很脆弱,需要进一步地巩固。一定要意识到,计算机科学的专业内容是知识,而编程本身则是一种技能,或者说一门手艺——它必须要通过大量的,反复的练习来提高。而最好的练习方式,就是刷题。这里要推荐@陈越姥姥 的 PAT https://www.patest.cn/——直接去 PAT 乙级的题库。
- List item
乙级的大部分题,都适合刚刚学会编程的读者们来做——完成这些“水题”可以有效增强自信,并实际地快速提高你的代码量,进而提高编程技能。需要注意的是,PAT 乙级的一部分题需要基本的数据结构和算法知识,现在你可以先不做——进入题库之后可以选择按通过率排序来依次完成。如果你遇到了困难,可以直接通过搜索引擎搜索 PAT + 题号,通常都能找到解法——你可以先看一遍解法,理解其思想,然后自己亲手写一遍。这里你会发现,虽然 PAT 支持 Python,但是几乎所有能找到的解题代码都是由 C/C++ 写成的——这也是我们推荐你通过 C 家族语言入门的原因。5、 可选:面向对象程序设计(C++)这门课程不用上完,只要看第一章就可以了——关键是理解编译和连接的过程,如何在命令行下编译,以及如何写 makefile ——这也是很多前辈推荐初学者从 Linux 入门的原因。然而事实上,根据最低耦合原则,这些内容本身并不是学习编程所必须了解的内容,而这些内容的学习本身也并不需要把时间浪费在学习 Linux 相关知识上——对于缺乏助教支持的读者来说,你完全可以等学会编程之后再去学习怎么使用 Linux。Windows 用户可以直接使用 MinGW、Cygwin 或者 Windows 10 提供的 Linux Subsystem,macOS 用户可以直接使用自带的终端——实际动手做一遍,用 cpp 文件编译一个可执行文件出来,建立这样一个最初的印象就可以了——接下来的学习可以仍然围绕着你原来使用的开发环境来展开。# 更上一层楼:数据结构与算法的学习语言关解决了,最基本的编程能力建好了,接下来你就可以进行数据结构与算法课程的学习了——正如我们前文所说的那样,这部分知识的性价比极高,在这一部分进行扎实地学习和充分的练习,可以让自己的编程能力拥有一个质的飞跃——可以说,对于真正想要从事开发工作的读者来说,这一步是绝对必不可少的。需要特别注意的是,《数据结构》跟《算法设计与分析》本质上是两门不同的课程——其层次不同,侧重点也不同。
所以入门的时候最好不要选择名字里把“算法”两个字放在前面的课程——例如 MIT《算法导论》公开课。相信我,如果你用它入门的话,那你一定会学得一头雾水的……
1、完成《数据结构》继续给陈越姥姥打广告……这门课程每半年开一次,分别在网易云课堂和中国大学 MOOC 上——作为入门课来说,这门课程是非常出色的,当你完成第一阶段的学习之后,你已经有了完成这门课程的能力。一边学习,一边练习——这门课程本身提供了一个单独的训练平台,里面的题最好全都做完——如果没时间的话,那就至少把课程中强调“一定要做”的题做完。
2、继续刷题我们还是以 PAT 为主——因为其他 OJ 平台通常都是面向 ACM 的,作为初学者来说接触它们仍然为时尚早。在学完栈和队列等线性数据结构之后,乙级剩下的题也都可以做了——乙级完成之后,可以直接开始进入甲级。等你把 PAT 刷完,你可以试试挑战别的 OJ 了——比如著名的 POJ
3、完成《数据结构(C++)》清华邓俊辉老师的数据结构,有兴趣的可以学一遍——和前面那门课程相比,这门课程难度更大,含金量也更高,但是入门门槛显然也更高,并且线上版课程砍掉了一部分关于图论的内容,这一点需要注意。
这门课程一共有 4 组共 12 道编程题,普遍有着巨大的难度(相对于之前做过的题),请大家量力而行。# 可选:学习C#和WinForm开发C# 作为 JAVA 和 C++ 的发展,是一种现代、先进而优雅的编程语言——现在你已经学习了 C++ 和/或 JAVA,如果你有 Windows 环境的话,那你可以试着学习一下 C# 和 WinForm 开发。C# 也是 C 家族语言的典型成员,通过学习 C#,你可以 实际地演练一下如何进行知识迁移,真切地感受目前自己已经拥有的能力——这个步骤,可以极大地增强你的经验和信心。
1、完成《C# 程序设计》这门课程介绍了 C# 的基本语法、WinForm 基本入门、委托(Delegate)和事件(Event)等一系列 C# 特有的特性——作为入门课程来说是合格的。这里需要给大家介绍一个小技巧——对于网易云课堂的课程,建议大家在电脑上安装一个安卓模拟器,然后使用客户端来观看课程视频——包括这门课程在内的很多老师的语速似乎都很慢,而云课堂网页版的播放器并不支持变速播放……2、学习《面向对象软件开发实践之基本技能训练》第一第二章@金旭亮 老师出品的课程,非常出色——把所有课程视频看完,照着写一遍所有示例,现在你已经可以写出小的桌面程序了。这个阶段看起来并不是必要的——但是这个阶段的学习,有以下三个方面的重要作用:实际体会一下如何进行知识迁移——这个时候你会发现,你虽然是第一次接触 C#,但是写起代码来却一气呵成,全然没有你想象得那么困难。如果你以后打算从事 Android 开发相关工作的话,那么你就会发现,你在这个阶段学习的很多内容都可以几乎无缝地迁移到 Android 领域。增强自己的信心——之前你的所有编程练习都是在黑乎乎的命令行下完成的,现在你终于可以开发桌面 GUI 程序了!这一步可以提供巨大的正向反馈,极大地增强你的信心。而 WinForm 虽然已经落后,但是在初学阶段这反而是它的优势——你只要学会最基本的 C# 语法、面向对象编程思想,并理解事件驱动机制就可以进行开发了,几乎不需要学习任何“没有用”的,无法迁移的知识。可以以很低的代价,快速开发出较为庞大复杂的程序——这个时候,你就会发现你最初写的代码已经很难维护了。这可以让你切身了解到软件工程、设计模式等等的重要性——并且以低廉的成本,给你一个可以演练它们的舞台。# 学习一种脚本语言当你已经学会了至少一种 C 家族语言时,你的核心编程能力已经初步构建完成——为了你的全面发展,有条件的话推荐你掌握至少一种脚本语言。我们建议选择 Python,当然你也可以选择 JavaScript 或者其他任何一种你喜欢的脚本语言。
这里以 Python 为例进行说明:
1、完成 MIT 6.00.1x(麻省理工:计算机科学和 Python 编程导论)。这门课程已经被很多人安利过了——现在你已经不是一个纯粹的菜鸟了,所以有些编程思想相关的内容可以跳过(但如果你对这些内容掌握不扎实的话可以复习一下)。重点应该放在以下内容上:Python 的基础语法Python 内置数据结构的使用方式Python 中的函数定义,如何使用Python进行面向对象编程如果你的 JAVA 或者 C++ 基础在之前的步骤中打得足够扎实的话,那么不管是 Python 还是 C#,你都可以做到只用几天甚至几小时的时间,就完成语言部分主要内容的学习——这对于初学者来说是完全不可想象的。知识迁移的威力,由此可见一斑。# 思想和方法:软件工程,设计模式和进一步的专业知识如果你之前用 C# 写过足够大的 WinForm 程序的话,那现在你手头多半已经有一个你亲手创造的烂摊子了——虽然它可以完成你想让它完成的工作,但每次给它添加什么新的功能,通常都要重写很多代码,而且很容易出 bug。这个时候,不用我说你也知道为什么需要学习软件工程和设计模式了。与此同时,如果你的编程能力只局限于 Windows 桌面本地单线程的程序,那显然也是不够的——你需要学习操作系统、数据库和计算机网络等相关知识。遗憾的是,网络上能找到的很多软件工程的 MOOC 都有种“文科化”的问题,缺乏与实际相结合的内容——所以我们不要直接去学习挂着“操作系统”和“计算机网络”之类的课程。记住,我们学习的目的是提高编程能力,而不是准备考试。
以下是按照由易到难的顺序排列的一条专业知识学习路线:
1、完成《软件工程:C 编码实践篇》这门课程讲了版本管理、代码风格、模块化设计等一系列知识,每周都有一些编程作业——整体来讲,这门课程唯一的缺点就是只在每年九月份免费开课一次,其他时间想上课(哪怕旁听)都要收费……而只看内容本身(先不考虑可能有的助教等资源,因为我没上过这课的收费版……),坦白地说课程收费还是贵了点……另外,这门课使用 Linux 平台,讲了基本的 bash 操作和 VIM 的使用方法——刚入门的时候你在技术上的原始积累还很不足,但 现在你已经成长到足够强大,可以开始学习Linux的相关知识了。如果没有机会上这门课,你可以直接阅读邹欣老师的《构建之法》,Linux 相关的内容可以在下面学习。
2、学习《面向对象软件开发实践之基本技能训练》第三到第五章,以及《面向对象软件开发实践之实战技能训练》第一章这部分主要讲了代码重构、测试驱动开发(TDD)和设计模式,跟《软件工程》课程有一点重合,但是更多的内容还是不太一样的。此外,这部分课程还讲了大量关于线程和计算机网络的相关知识。特别需要说明的是,这部分内容与 WinForm 的关联并不大——所以 Mac 用户可以直接安装.Net Core 和最近刚刚发布的Visual Studio for Mac(或者已经发布快两年的 VSCode)来练习编写课程中给出的例程。当然你也可以自己寻找 JAVA 中类似的内容。
3、学习《基于 Linux 的 C++》如果你已经学习了 C++ 的话,可以直接从第十二章开始,这门课程讲解了很多 Linux 的相关知识,以及操作系统和计算机网络的重要概念(进程、线程、Socket 等)——不过直接看的话可能会遇到一点障碍,因此你可以参照一些概念性很强的操作系统/计算机网络课程来学习。注意,不要学习学堂在线上的《操作系统》课程——它的难度系数过大,已经完全超过了 MOOC 课程体系能容纳的极限。事实上,在没有足够的助教资源支撑的情况下,想完成这门课程对于大多数人来说几乎是不可能的——哪怕按需选择章节都是浪费时间。
在这里,你将再一次体会知识迁移的高效性——Linux 提供的 POSIX C 语言线程函数,C++11 提供的 thread 类和 .Net 提供的 Threading 系列方法,虽然它们各自诞生的年代和所属的平台都有巨大差别,但是使用方法上却存在着惊人的相似性。如果你使用 JAVA 的话,你也可以自己寻找关于 JAVA 多线程控制的内容——然后你会发现什么叫做“万变不离其宗”,并且能够意识到“程序员吃青春饭,技术岗位干不久”这样的说法为什么是错的……操作系统和计算机网络部分补充阅读:《Linux C 一站式编程》根据自己的需要,按需学习。这部分特别介绍了 gdb 的使用方法。之前借助 Visual Studio,你应该已经学会了如何对一个程序进行调试——所以这里你可以非常容易地掌握 gdb 的用法。《CS241 System Programming》这个是美国伊利诺伊大学香槟分校(UIUC)的操作系统课程使用的教材——如果你英语水平不太差,并且对一些基本的概念(比如进程,线程)已经有了了解的话,那么它会比国内的教材更易懂。其他:完成《数据库系统原理》或者《数据库系统概论》对于数据库,你需要了解数据库的基本概念和原理,并且要掌握 SQL 语言的使用方法——这两门课程的内容目前看来都是可以满足要求的。这门课程并不依赖于以上专业课,你可以在任何时候开始学习。