今天早上看到一篇文章《笨功夫是普通人最后的依靠》,有感而发,文中说的内容都是自己现在的一些想法, 本想在下面评论一下,但是好像要说的太多了,评论写不下,也就有了本文。
背单词是学英语的 “笨功夫”
故事还得从差不多十几年前的初中说起,在我上小学的时候,所在的小学是还没有教英语的,所以我的英语是从初中开始学的。 还好初中上英语课的时候,老师依然是从 26 个英语字母教起的,只是会有种错觉,那些小学学过英语的同学好像不用怎么学就能在英语上获得好成绩。 但这种想法不太客观,因为在有这种想法的时候,潜意识中将他们小学几年的积累忽略了,所以多少会有点沮丧。
九边的文章中,提到了学习英语得先背单词,背单词就是学习英语中的 “笨功夫”。对于这点深有体会,虽然我的英语是初中开始学的, 而且我对语法可以说知之甚少,但是我在背单词上可是花了不少时间的,所以英语成绩也不至于太难看。 后来上了大学,也能凭借初高中的英语单词的积累,考过了四级。 然后做为一名程序员,日常开发的时候需要看到英文文档也可以比较流畅,当然肯定不如看中文顺畅。 说这些,并不是觉得这是什么光荣的事,只是想表达比较认可背单词是学习英语的 “笨功夫” 这一观点。
笨功夫之外,方法也重要
几年前看极客时间的《数据结构与算法之美》这门课的时候,提到了一点:
数据结构和算法就是一个非常难啃的硬骨头,可以说是计算机学科中最难学的学科之一了。 我当时学习也费了老大的劲,能做到讲给你听,我靠的也是十年如一的积累和坚持。如果没有基础、或者基础不好,你怎能期望看 2 个小时就能完全掌握呢?
九边的文章中也提到了自己的经历,想学写代码,然后一推人推荐学习《算法导论》。 对于这个,我个人也是深受其害,十几年前我一直徘徊在玩游戏和学习之间,常常觉得自己的时间不应该全部还在玩游戏上,怎么也得学习一下。 然后我就会去学习,我怎么学习呢?也是跟九边一样,找那些前辈们推荐的经典教材,比如算法、操作系统、编译原理、计算机网络相关的经典书籍,
依然记得我在高三的时候就买来了一本《编译原理》,也就是那本 “龙书”(因为封面是一条龙)。 但是,这本编译原理就算让现在的我去看估计也很难看懂,所以在学习方面,个人其实走了很多弯路,跌了不少跟头。 因为学习的方法不对,这种学习自然是持续不下去的,在这种学习过程中,需要耗费大量的心力,然后自我怀疑,最后放弃。
对于这一点,九边的文章中也提到了背后的根本原因:
- 选错教材了,你拿得太难了,不适合你;
- 投入时间不足。
这两点都是我当时存在的问题,一上来就选择了最难的教材来看,没有考虑到自身实力能不能看得懂。 当然,选择难的教材也不是不行,可能得投入足够的时间去把教材看懂,前提是,有途径可以解决学习过程中的遇到的问题, 比如遇到问题可以问问前辈,又或者像现在的 GPT,如果想借助百度这种东西可能大概率要失望。 跳过少数问题可能不影响学习的效果,但是如果绝大部分问题都没有能找到答案的方法,那换一本简单点的教材先学学基础是个更好的方法。
当然,说这些并不是为了鼓励大家去学习数据结构算法,只是想说,在我们学习遇到困难的时候,可能得考虑一下是不是上面两个原因导致的。
大脑对熟悉的东西更感兴趣
这句话已经不记得是从哪里看到的了,但是觉得颇有道理。 有时候,我们对某件事情不感兴趣是因为不擅长,而不是因为真的不擅长。 在进入一个相对新的领域的时候,我们会接触到很多新的名词、术语,会觉得特别费劲。 这个时候我们可能就会选择放弃了,当然,我们也可以选择坚持下去, 这些年我学到的一个非常重要的学习方法就是,对于新接触的东西,去多看、多想、多实践几遍,当然,这里说的是学习编程领域的东西。 很多东西,在我们这样几轮学习下来,好像其实也没有太难的东西,当然也有可能我学习的东西本身没有很难的东西。 但不得不承认,就算是那些你觉得不难的东西,很多人也潜意识中会觉得自己学不会, 但实际上只是他们投入的时间还不够多。
在开始的时候,我也会觉得枯燥无味,但是在熟悉多几遍之后,发现好像还挺有意思,尤其是使用新学到的东西解决了自己实际遇到的一些问题之后。 学习的过程大多如此吧,说到这,想起了几天前看到的《如何取得杰出成就》中提到的一点:
有一些工作,我们可能必须在自己讨厌的事情上努力工作数年,才能接近喜欢的部分,但这不是杰出成就产生的方式, 杰出的成就是通过持续关注自己真正感兴趣的事情来实现的 —— 当我们停下来盘点时,会惊讶于自己已经走了多远。
这篇文章是《黑客与画家》作者博客《How to Do Great Work》的翻译版,有兴趣可以看看,感觉还不赖。 在实际工作中,我们遇到的很多问题其实并不需要坚持数年才能解决,又不是研究什么前沿领域的东西, 但是对于一些难题需要花几天或者一两个星期去解决这种可能性还是很大的。 在这个过程我们会对问题域中的东西越来越熟悉,直到最后它们不再是我们的障碍。
问题是可以分解的
搜狐 CEO 张朝阳这几年在 B 站上更新了很多物理的教程,当然我是全都看不懂,只是想起他说过的一段话:
很多东西的话,就是你看起来很复杂,是因为你不熟悉,其实这个知识, 天下的这个知识和所有的东西,其实都是不难的,你把所有的再复杂的东西,把它分解成每一步的话, 他的基本的这个思维过程的,跟你早上吃什么饭,怎么做饭,怎么打车怎么点东西,都是一样的思维过程。 很多东西你理解不了,不是因为你笨或者是你不够聪明,而是因为你,你自己认为你理解不了是吧, 很多可能因为过去的经历啊,就是在课堂上这个回答不了问题啊,一些挫败的经历,考试的失败导致, 你就有一种恐惧,是一种恐惧和你的认为理解不了导致你理解不了。
虽然道理是这么个道理,但是不代表物理都没学过的人能看得懂他讲的物理课, 因为问题虽然可以分解,但是一个需要有深厚基础的问题恐怕你连分解都不知道怎么分解,更不要提解决。 就好像上文提到的《算法导论》这本书,里面有大量的数学推导,很多人看不懂是因为, 从作者的角度来说,他已经把问题分解为了若干个小问题,在他看来,看这本书的读者应该是已经掌握了他分解之后的问题的相关知识。 从推荐看这本书的人来看,他推荐的对象应该也掌握了书中那些分解之后的知识。 但是实际是,可能也有很多人没有考虑到自身实力,然后就去啃这些大部头,自然而然地看不懂。
很多时候我们遇到的问题都能找到恰当的分解方法,尤其是编程领域,要不然我们不大可能会碰到那个问题。 在摸爬滚打多年之后,我们会发现,很多那些入行时觉得困难的点最后都不是问题了, 这是因为,常见的问题我们基本都解决过一遍了,以致于我们再遇到同样的问题之后,就能马上想到应该怎么去做,就已经在心中有一二三四几个步骤了。 举一个例子,在学习做 web 应用的时候,其实很多东西都不懂,但是现在已经很清楚一个 web 应用大概应该是长什么样子的了:
- 从浏览器发起的请求到达 web
应用之后,我们需要确定具体执行什么逻辑,因此需要有 “路由”
来将请求拍发给一个具体的方法,也就是某个
Controller
里面的一个方法。 - 在请求的处理逻辑里面,我们可能需要去查询数据库,所有常用的 web 框架都提供了关于数据库查询的一些抽象,直接调用封装的那些方法即可。
- 在返回的时候,我们要返回给客户端的实质上是纯文本的东西(
HTTP
协议),但是HTTP
相关的功能往往由HTTP
服务器来处理的,比如 nginx - nginx 处理
HTTP
相关的东西,比如反向代理的upstream
返回的数据长度有多长,需要算出来,将这个长度写入到HTTP
头中,这样浏览器收到HTTP
报文的时候才能准确地解析出一个HTTP
报文包
弄清楚这些问题之后,不管换哪一种语言,我们都可以拿来实现一个 web
应用,无非就是解析 HTTP
报文,在代码里面做一些业务逻辑处理,然后返回一串 HTTP
报文。 而这里提到的,其实就是针对 web
应用开发中的几个大问题的分解,这些问题对于写了几年 web
开发的人来说其实都不是问题了。
再举一个例子,对于程序员来说,我们往往需要持续地学习,当我们去学习一些新的编程语言的时候,我们可以去思考一下:对于编程语言来说,它可以分解为哪些问题?
个人感觉,这个问题其实挺有价值。要回答这个问题,我们可以回到没有今天这些高级编程语言的时候,那些计算机领域的先驱们是怎么让计算机工作起来的。
我们会发现,其实一开始他们是用的 0 和 1
来去写指令的,后面进化到汇编语言,毕竟一堆 0 和 1 谁记得住?
有了汇编,去做一些操作就简单多了,比如做加法,用一个 ADD
指令就可以了。
但是有了汇编之后,还有一个问题是,不管是从开发、维护上来说,都需要对 CPU
有非常清楚的了解,比如需要了解 CPU 有哪些寄存器之类的知识,
也就是说,使用汇编依然需要了解机器本身的很多运作机制,这无疑是一个比较高的门槛。
再后来到 C 语言的出现,我们就不需要了解 CPU
是怎么工作也可以写出能解决问题的代码了。 但是 C
语言依然有一个毕竟严重的问题,那就是需要开发者手动去申请内存,使用之后再释放内存,如果程序员忘记释放,那么就会造成内存的泄露。
所以更高级的一些语言就有了
GC
,也就是说,由语言底层的运行时去帮程序员回收那些已经不再使用的对象。
扯得有点远了,回到问题本身,对于编程语言来说,它可以分解为哪些问题? 这个问题其实非常简单,只要我们随便找一门编程语言的教程来看它们的目录就会知道,一门编程语言本身包含了:
- 一些基础语法:如代码组织结构是怎样的。Java 是通过一个个的类来组织的,Go 是通过结构体来建立抽象然后通过函数来进行组织的。
- 对于面向对象的语言来说:不同的编程语言会有不同的类的编写方式。
- 基本的变量定义是如何定义的
- 关键字有哪些,比如非常常见的
class
、public
、def
之类的 - 如何实现循环结构
- 如何实现条件判断
- 如何在方法中返回值。有些语言需要使用
return
,也有些语言比较省事,方法内的最后一行会被当做返回值,比如ruby
。 - 一些常用的数据结构是如何封装的。比如数组、
map
等 - 标准库。比如如何执行一个系统命令这种功能。
- 其他...
这个清单不太完整,但是也足够了,因为编程语言的相似性,我们在熟悉了某一门编程语言之后,往往也会比较容易学会另一门编程语言。 但是这也并不代表,我们可以使用这门新的编程语言去解决一些实际的问题,除非,在此之前,我们已经使用了其他编程语言解决过相同的问题了。 比如,我们从 PHP 转换到 Go,我们在 PHP 中已经解决过很多数据库查询的问题了,切换到 Go 中,对于数据库查询的这一问题,我们可以作如下分解:
- 找到 Go 中查询数据库相关的库
- 调用库函数来建立到数据库的连接
- 调用库函数来发送一个
SQL
查询语句到数据库服务器,然后等待数据库服务器返回查询结果 - 取得查询结果,做其他处理
清楚了我们需要解决的问题之后,其实我们真正要解决的重要问题是如何组织我们的代码,从而使得我们针对这个问题的解决方案更好维护、能更好地使用。 所以现在在学习的时候,更喜欢从实际的问题出发(毕竟计算机领域其实是更偏向于实践)。 然后根据自己拆分后的问题去找解决方案,事实证明,这样效率更高。 如果我们从技术本身出发,我们可能无法知悉这个技术为什么是今天这个样子的,在这种学习方式之下, 我们新学习的东西无法跟我们脑子里原有的东西建立起连接,最终只会很快就忘记。 但是如果从我们熟悉的问题出发,去寻找一种新的解决方案的时候,其实新的知识跟自己的经验是可以很好的联系起来的,这样我们通过一个问题就能联系到不同的解决方案。
真的扯远了,说回正题。说这么多其实就是想说,碰到难题的时候我们也不能盲目地花笨功夫, 遇到难题的时候,我们也许可以考虑一下,这个问题可以如何分解,然后如何解决分解之后的问题。 如果分解后的问题是我们可以解决的,那我们的 “笨功夫” 那就是使用对了。
学习是为了找到学习方法
再说一个关于 “笨功夫” 的个人经历,还是初中的时候,在初中的时候花了很多时间在学习上,但是学习效果并不是非常明显, 多年以后,才明白,自己当初的那种学习其实是 “死学”,也就是不讲究方法的学习,免不了学了就忘。 初中的时候一个物理老师跟我们说他学生时代,有一天在思考问题很久之后突然 “开窍” 了, 以前没懂,现在知道了他说的 “开窍” 大概是找到了关于学习的套路。 可惜的是,我在读书的那十几年里,并没有经历过这样的 “开窍”,所以成绩一直平平无奇。
直到自己工作以后,因为自己从小到大是那种不太擅长交流的人,所以工作前几年遇到问题的时候也基本不会去请教别人, 那怎么办呢?那就自己想办法去解决各种技术问题呗,然后几年下来,好像自己的学习能力有所提升了,明显的表现是,学习新东西的时候会学习得更快了。 后面才懂,越来保持学习其实不只是为了学到各种解决问题的方法,实际上有很多东西都是学了之后用不上的,更重要的是在这个过程中学会如何学习。 关于这一点,陈皓有过一个经典的陈述。
学习不仅仅是为了找到答案,更是为了找到方法 - 陈皓
你有没有发现,在知识的领域也有阶层之分,那些长期在底层知识阶层的人,需要等着高层的人来喂养, 他们长期陷于各种谣言和不准确的信息环境中,于是就导致错误或幼稚的认知, 并习惯于那些不费劲儿的轻度学习方式,从而一点点地丧失了深度学习的独立思考能力,从而再也没有能力打破知识阶层的限制,被困在认知底层翻不了身。
可见深度学习十分重要,但应该怎样进行深度学习呢?下面有三个步骤:
- 知识采集。 信息源是非常重要的,获取信息源头、破解表面信息的内在本质、多方数据印证,是这个步骤的关键。
- 知识缝合。 所谓缝合就是把信息组织起来,成为结构体的知识。这里,连接记忆,逻辑推理,知识梳理是很重要的三部分。
- 技能转换。 通过举一反三、实践和练习,以及传授教导,把知识转化成自己的技能。这种技能可以让你进入更高的阶层。
这就好像,你要去登一座山,一种方法是通过别人修好的路爬上去,一种是通过自己的技能找到路(或是自己修一条路)爬上去。 也就是说,需要有路才爬得上山的人,和没有路能造路的人相比,后者的能力就会比前者大得多得多。 所以,学习是为了找到通往答案的路径和方法,是为了拥有无师自通的能力。
把时间当作朋友
这个标题来源于李笑来的《把时间当作朋友》这本书,书买了我还没看,但是看过他在得到的课程上这一话题的相关文章。
今天这个社会变得越来越浮躁,我们难免会受到影响,经常会想着今天做一件事,明天就能看到成果。 但实际上,在竞争激烈的今天,聪明人很多,又聪明又努力的也有很多,我们能做的只是接受这个事实, 然后持续在自己所在的领域花多一点 “笨功夫”,把时间当作朋友,就算最终我们没有实现最初的目标, 但是回头再看的时候,会发现原来自己已经走得很远了。
最后,用吴军《格局》中的一句话来结束本文:
事实上,功夫没下够,用什么方法都是在浪费时间。