乐趣区

有一种执着叫我要学编程

10 岁前学编程,要么是被爸妈逼的,要么是学习任务;

20 岁前学编程,除了兴趣,就是为了竞赛加分;

30 岁前学编程,除了学习,就是为了找工作;

40 岁前学编程,纯粹为了生存;

40 岁后学编程,是一种执着。

有人后悔当初没有选择计算机专业,没有学会编程;

也有人后悔当初选择了计算机专业,成了一代码农;

有人因为羡慕黑客,选择了程序员的职业;

也有人因为喜欢挑战,开始了编程的自学。

不论是何种原因,选择了“学编程”之路,都会经历一系列的过关斩将。

铺天盖地的编程培训广告,只会告诉人们入门有多容易,却不会讲它有多难。

结果大多都是,满怀希望,一腔热情,终于湮灭。

交了几千学费事小,丢了好奇心,才是个人最大的损失。

为什么编程那么难学?我们从学习说起。

什么是学习?

关于学习,我们大部分的时间都是在学校完成。

“好好学习,天天向上”,是贴在教室墙上最显眼的金句广告。

读小学时,那些勤奋的人,每到期末都会得高分,成了老师和家长眼中的明星。

勤奋,在大部分人眼里,成了通往成功的必经之路。

小小年纪,恨不得每天“学习”25 小时,一个个被逼出厚厚的眼镜片。

上了初中,成绩似乎变得挂不住,一直往下掉。

通宵达旦的努力,有时候还不如小部分人临时抱佛脚。

“悬梁刺股”这个故事更像古人的励志鸡汤。

有教学经验的老师,会强调“学习方法”,相比“死记硬背”,更要懂得找规律。

每个人的记忆容量是有限的,但现实情况的组合却是无限的。

比如,我们背九九乘法表,但不会背多位数乘法表,因为数字是无限的。

相反,我们会观察乘法规律,设计巧妙算法来解决具体算数问题,比如“列竖式”。

实际生活中,我们要解决的问题也有无限种情况,光靠记忆无法解决所有问题。

于是,我们会慢慢从生活经验中,发现规律,提取知识,再应用到未知情况中检验。

这个过程就是学习,它让我们可以用有限经验,应对无限可能,用知识压缩庞大的信息。

人的“聪明”程度,取决于需要多少经验获得规律,也即“悟性”。

靠记忆学习,可以获得线性的知识累加,学一点得一点;

用规律学习,可以获得指数级知识增长,通一点得一片。

这样对比,并不是说记忆不重要,相反它在知识积累过程中非常重要!

在没有背九九乘法表前,我们和英国前首相卡梅伦一样,是算不出 9 乘 8 是多少的;

在没有背那堆数学公理前,我们无法用高度抽象的思维来理解世界,更不会有现在的计算机世界;

在没有背基本语法和句式前,我们无法从历史著作中了解当时的人类社会,更无法继承和传承已有的知识。

所以, 学编程遇到的第一道坎,就是记住已有语法,它就像是驱动魔法的咒语。

都是一些英文单词,学过英语就知道,单词可以背,但理解语义是关键。

比如,“interesting”在词典里的语义是个形容词,表示“有趣的”;

但如果放到实际对话中,它可能就代表相反的意思“无聊”,和我们经常挂嘴边的“不错”、“挺好”类似。

编程语言,同样用英文单词表示,但背后语义比法典还严格,每个单词在任何语境下都有明确意义,无歧义。

而单词背后的意义,往往是初学者最难迈过的坎,几乎只能“死记硬背”。

原因也很简单。

英语是人与人的交流,我们只要理解了对方的文化习俗,沟通就会顺畅。

比如在一起生活一段时间,哪怕不能用完整句子表达,只说几个关键词,对方就能秒懂。

但,编程语言是人与机器的交流,你需要给它明确指令,它也会给你明确反馈。

机器建立在 0 和 1 的二元世界,组成它的部件都是硅和金属,部件之间靠电子脉冲沟通。

人与机器最早沟通也是从二进制开始的,那时候计算机叫打孔机,工作原理就像《无间道》里梁朝伟给黄秋生发情报。

略去几十年计算机发展史,现在计算机的主要硬件,如 CPU、内存、硬盘等,都会通过操作系统这个“资源管家”管理。

人与机器沟通方式主要有两种:1)图形化界面交互;2)指令接口。

第一种我们日常都在用很容易理解;第二种指令接口,就是我们所说的编程。

几十年前,操作系统并没有那么完善,我们还得通过低级语言和机器沟通,比如二进制、汇编等;

如今,我们可以站在操作系统之上,快速调动机器资源,比如计算能力、网络通讯能力、存储能力等。

编程语言,也变得越来越“高级”,并不是说语言的优劣,而是抽象层次更高,更接近人类语言。

比如,C 语言中定义一个变量,你需要明确指出它的数据类型,是整数,还是小数(浮点数),还是像“HelloWorld”这样的字符串。

于是 C 语言中会有很多关于数据类型的关键词,比如 int、long、float、double、char 等。

要理解这些英文单词,除了背,还得理解语义,也就是背后操作系统的内存分配规则。

所以,学 C 语言难,是因为除了语言本身,还得了解更多关于操作系统内部的资源管理规则。

对于大部分人而言,学编程并非为了给操作系统这样的基础设施添砖加瓦,而是解决现实工作生活的一些实际问题。

比如,批量处理表格、自动排版、定时发邮件等,提高工作效率;

比如,自动抓取数据、搭建数据分析系统,增强信息情报处理能力;

比如,开发小程序、小应用、小工具,以功能形式输出自有专业能力。

于是,“高级”的 Python 以更低的学习门槛,成了普通人的选择。

它有 30 多个关键词,每个词都不难,但想记住后流畅使用也不容易。

如果有人天生会编程,那他一定来自《星际迷航》中的瓦肯星。

编程的目的,是用数学驱动机器资源解决实际问题。

所以, 学编程遇到的第二道坎,就是学会问题抽象。

这也是阻碍普通人学编程最大的门槛,光靠记忆无法学会编程。

抽象,就是从具体问题里提取出共性的特征,也就是文章开头说的“找规律”,然后就可以套用已有知识解决问题。

抽象能力,决定了每个程序员的编程水平,也决定了每个人所能解决问题的宽度。

学编程,“Hello World”几乎是每个教程的第一步,这代表着你已经把工具环境准备到位,可以开始。

当你用编程语言解决某个具体问题时,就算正式入门。

比如,“输入 2 个不同的整数,输出较大的那个。”

这样一个判断大小的题目,你自己甚至可以立马给出答案。

但如何用编程来解决?

从这里开始, 你就得学会把问题分解到最小的执行步骤。

比如,编程里的赋值操作,“x=1; y=2”,把两个数分别放到两个变量,变量代号 x 和 y。

接着,利用数值比较和逻辑判断来比较大小,最后,输出那个大的数打印到计算机屏幕,“print(x if x > y else y)”。

这样看来,似乎编程并没有给我们带来好处,反倒增添了不少麻烦,这也是很多初学者一开始会有的疑惑。

虽然这只是个数学题的例子,但如果放大规模,执行 1 万次,你就会发现编程的优势。

到这里,也许你还没有感受到抽象的威力,那就再来一个案例,“把 10 个整数从小到大排序”。

先不用考虑最优解法,我们用最朴素的算法来解决这个问题。

  • 第一步,把 10 个数字放到 10 个变量;
  • 第二步,选择其中一个变量 x,和所有没被选择过的变量比较大小,如果发现有比它更小,就把两者的数字互换;
  • 第三步,把 10 个变量都按第二步操作一遍;
  • 最后,按选择顺序把变量一个个打印出来,就是 10 个数字从小到大的序列。

10 个数的排序问题可以这样解决,那么 1000 个数,10000 个数呢?

只要我们把算法设计好,规模只是一个变量的修改而已。

此外,这个问题第二步中的“比较大小”,和上一个案例一致,也就是说,上个案例是这个案例的子问题。

无论问题大小,我们都可以分解得到一系列的子问题,当子问题都有了具体解法,整个问题也就有了答案。 这就是分而治之的思想。

比大小、排序,这些都是抽象概念,和现实世界的具体问题之间还有一段距离。

那就再举个例子。

我们去淘宝买东西,经常有个习惯,在搜索关键词后,会按销量排列,这样就可以大致了解商品有多少人买过,至少可以规避掉一部分购物风险。(至少我是这么干的)

“按销量排列”,其实就是一次排序过程,只不过除了排序算法外,还得解决展示和交互等子问题。

当然,淘宝页面的排序算法也绝不仅仅是上面描述的那么简单,但背后的思想却是相同的:分而治之。

有了抽象思维,就更容易理解编程里的概念。

如果你多观察几门编程语言,会发现它们大都包含下面这些内容:

1、变量和基本数据类型;

2、数值和逻辑计算;

3、条件判断和循环;

4、函数和类;

5、基本输入输出;

6、多线程 / 多进程;

7、图形化、网络、数据库等。

其实,1~4 是语言的核心,5~7 是语言调用操作系统资源的封装,也就是语言在应用时的扩展。

就像上面举的案例,当我们要连续存放 10 个整数,可以用 10 个变量,也可以用一个“数组”变量,这是编程语言提供的基本数据结构支持。

数组可以方便用索引来遍历内容,免去我们写 10 次变量名的尴尬。

Python 语言里的数组是 List,比如“x=[1,2,3]”,还可以通过切片等操作更方便地操作数组。

这些都是“高级”语言提供的内置支持,就像你买 Macbook 自带触摸板而不用再购买鼠标,一个意思。

但,想要用好这些内置的特性,得知道什么时候适合用,用的时候有什么限制。

这就是为什么“数据结构”和“算法”那么重要。

当然,对于非职业程序员,掌握基本的数据结构和算法就已足够,更多情况下是调用别人准备好的“库”。

当我们语法背的滚瓜烂熟,也能按葫芦画瓢写点输入输出的命令行程序后,我们开始尝试着手解决实际问题。

但实际情况是: 没有抄的对象,就写不出代码;出了问题,不知道从何查起。

于是,高昂的热情,一次又一次被“错误提示”浇灭。

学编程遇到的第三道坎,是持续精进。

回到学习本身过程。

上学时候, 我们的学习是由四部分组成: 看书、听课、做题、考试。

  • 看书,是自学过程,自己理解和感受知识;
  • 听课,是老师对知识的演绎,用更多个性化的案例让学生“懂”;
  • 做题,是知识强化过程,输入问题,输出答案,每一题都在强化知识在大脑的存在;
  • 考试,是知识应用的检验,用未曾输入过的问题去检验大脑中的知识,看能否得出正确答案。

其中,听课、做题、考试是主旋律,看书往往是辅助而非强制。

这就让我们长期处于被动学习过程,一旦脱离“四件套”,就会找不到方向。

当然,这也造就了万亿级的成人教育市场。

学习过程,就是发现规律,强化规律,检验规律的过程。

还是拿上面的排序为案例,第一次理解排序有点吃力,第二次好像懂了点,第三次,第四次……

在一次次重复理解的过程中,可以用不同的 10 个数来检验,就像做数学题一样,加强自己对这个“规律”的理解。当我们遇到新的这类问题,能自然想起套用这条规律解题时,我们就真的掌握了它。

每个子问题可能都不难, 难的是因为没有真正掌握透,所以在实际应用的时候并不能熟练驾驭。

比如,经常有培训课教人学 Python 爬虫。

如果已经过了第二道坎,很容易就能看透爬虫的本质。

首先 ,爬虫解决的是自动数据获取问题,取代手工操作,更快更省力。

其次 ,爬虫问题可分解为 4 个子问题:1)模拟用户访问;2)抽取所需数据;3)保存数据;4)自动翻页或加载更多。

能解决 4 个子问题,就能解决爬虫问题。

  • 对于临时爬虫需求,写代码解决这个问题并不是最好的,因为花的时间多,收益却非常有限。通过浏览器插件,如 WebScraper,或者不需要自动翻页的情况下,用 Xpath 也可以很好解决。
  • 对于每天都要获取数据的情况,写代码会比较划算,因为花一份时间,可以持续每天获益。

最后 ,打开每个子问题,再划分出更细更具体的子问题。

  • 比如,要用代码实现模拟用户的访问,就得先了解什么是 HTTP 请求,了解用户访问的流程;
  • 比如,要提取数据,就得了解 HTML 的网页结构,了解如何用代码解析网页获取具体标签下的数据;
  • 比如,数据保存在哪里,文本文件还是表格,或者是数据库,不管在哪储存,都得了解如何用代码写入数据;

以上所有的知识,都在编程语言之外,属于应用范畴。

此外,在执行过程中,还会遇到各式各样、稀奇古怪的“错误提示”,每个错误可能需要花几个小时才能解决。

从投入产出比看,学编程的早期,时间效益实在太差了。

长时间“无回报”式的投资,很容易让人选择放弃。

但那批坚持到破茧而出的人,仔细观察他们前后的变化,你会发现:

  • 他们自学能力变强,擅长捕捉到事物内在规律;
  • 他们看问题更透彻,这是逻辑思维训练的结果;
  • 他们善于主动搜索,这是被错误调试逼出的能力。

学编程并不是为了当程序员,但每一个优秀程序员,都具备超越常人的能力。

所以,对于初学者而言,需要直面编程的三道坎。

第一道坎,掌握语法,这是通往计算机世界的钥匙。

第二道坎,学会问题抽象,这是解决问题的高级方式。

第三道坎,保持精进,在持续解决问题中获得成长。

工作以结果为导向,但能力却是在过程中锻炼而成。

不要去相信“X 天学会编程”的广告语,那些只会无限抬高自己的预期,当达不到时更容易放弃。

那么编程到底该怎么学?以 Python 为例:

1、 选教材 ,《A Byte of Python》,中文翻译叫《Python 简明教程》,直接搜索就可以找到;

2、 选习题 ,Github 上有个《Python – 100 天从新手到大师》,照着练习做题,先学会基本的 Day1-Day7;

3、 练项目 ,选择一个应用方向,比如图形化、Web 网络、数据分析等,然后再从上面的习题挑选对应内容练习;

4、 找答案 ,出错了,遇到问题了,先学会搜索(99.9% 的问题都有现成答案),再找人问。如果去各大技术论坛提问,记得先看看有没有人提过类似问题。

当然也可以在这留言,我会尽力提供帮助:-)

先写到这里,后面有机会再展开讲具体内容。

如果这篇文章对你有帮助,记得帮我点个赞,谢过~


文后思考:

不管是科学还是人文,终将会把人拉回哲学的思考。

人文更关注具象的个体,科学更关心内在规律。

虽然我们可以从世界中提取规则,但每个个体都是独一无二的存在。

有的人擅长理性思考,轻松洞察到深层次的规律;

有的人擅长艺术创作,用感性作品影响更多个体。

但不管是理性还是感性,终将回归现实,解决或近或远的发展问题。

END

退出移动版