还在读书,也在实验室帮忙做了些东西,自己也搭过几个网站。在周围人看来似乎好像我很厉害,做了那么多东西,但是我发现这些东西虽然是我做的,但是实际上我手把手自己写的代码却并没有多少,很多都是用开源的东西,我写的代码无非是把别人的东西整合下,类似于胶水一样的工作。
我之前所认为的编程是全手动一行一行敲代码,但是现在我发现哪怕是工程上也有很多人是复制黏贴来解决问题的,并且提倡不要重复造轮子。
但是靠谷歌和复制别人的轮子,虽然我做出了很多东西,可是我并不觉得自己能力上有提升,倒是利用搜索引擎的能力的确提升了不少。而学校里另外一部分在搞ACM的人,他们每天都在刷题练算法,但单凭我个人的感受感觉他们似乎对工程上有些东西并不了解,或许算法的能力才算是实打实的编程能力?那"胶水"的能力和整合轮子的能力算不算编程能力呢?
所以我现在就很困惑,所谓的编程能力到底是什么,我该如何提升自己的编程能力?
================== Update ======================
距离提这个问题一年多过去了, 现在我也刚到了大三,感谢知乎上的各位老师们的认真答题,大部分回答我都一一看了, 这一年里经常看到微博上不时有人转发这个问题,并表示有同样困惑。但经过这一年的成长, 我大致上也想明白了这个问题, 所以想说说我个人的理解。
我先说下我目前的编程工作流, 假如我想实现某个功能, 我会先去搜下类似轮子, 看下star, 和更新频率,都可以的话再去看他代码是否靠谱。经过这套流程很容易判断一个轮子是否靠谱 , 靠谱就用, 不靠谱再想到底是改代码提交PR还是直接重写。
我问题里的"胶水"能力其实就是一个人编程素养的综合体现。一个合格的工程师应该是用最低的资源实现最大的效益,所以所谓的造不造轮子只是一个具体决策而非一种死磕的价值观。但是无论是造轮子还是用轮子, 对我而言有一个天条就是, 我必须对我使用的轮子有足够了解我才能去使用它,如果没有,我就必须花力气去了解他。否则,很容易就成为一种API程序员, 比如你可能用各类API实现了一个人脸识别的App,然而事实上你对人脸识别技术一无所知。不出问题还能装装逼,出了问题就跪了。
所以我理解的轮子, 只是为了简化我的体力劳动或辅助我的智力劳动, 但如果替代了我的智力劳动, 让我纯粹做了体力劳动那就没意思了。当然仅就个人学习过程而言, 进了公司自然目标和境况都不同了也不是个人能够决定的了的了。
作者:h8liu
链接:https://www.zhihu.com/question/31034164/answer/50423838
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
非常好的一个问题。这可能是我在知乎见到过的问编程有关的问题中问得最好的一个了。我非常喜欢这个问题。
计算机科学有两类根本问题。一类是理论:算法,数据结构,复杂度,机器学习,模式识别,等等等。一类是系统:操作系统,网络系统,分布式系统,存储系统,游戏引擎,等等等等。
理论走的是深度,是在追问在给定的计算能力约束下如何把一个问题解决得更快更好。而系统走的是广度,是在追问对于一个现实的需求如何在众多的技术中设计出最多快好省的技术组合。
搞ACM的人,只练第一类。像你这样的更偏向于第二类。其实挺难得的,但很可惜的是第二类能力没有简单高效的测量考察方法,不像算法和数据结构有ACM竞赛,所以很多系统的苗子都因为缺少激励和正确引导慢慢就消隐了。
所以比尔盖茨才会说,看到现在学编程的人经常都把编程看作解各种脑筋急转弯的问题,他觉得很遗憾。
做系统,确实不提倡“重复发明轮子”。但注意,是不提倡“重复发明”,不是不提倡“重新制造”。恰恰相反的,我以为,系统的编程能力正体现在“重新制造”的能力。
能把已有的部件接起来,这很好。但当你恰好缺一种关键的胶水的时候,你能写出来吗?当一个已有的部件不完全符合你的需求的时候,你能改进它吗?如果你用的部件中有bug,你能把它修好吗?在网上繁多的类似功能的部件中,谁好谁坏?为什么?差别本质吗?一个开源代码库,你能把它从一个语言翻译到另一个语言吗?从一个平台移植到另一个平台吗?能准确估计自己翻译和移植的过程需要多少时间吗?能准确估计翻译和移植之后性能是会有提升还是会有所下降吗?
系统编程能力体现在把已有的代码拿来并变成更好的代码,体现在把没用的代码拿来并变成有用的代码,体现在把一个做好的轮子拿来能画出来轮子的设计蓝图,并用道理解释出设计蓝图中哪些地方是关键的,哪些地方是次要的,哪些地方是不容触碰的,哪些地方是还可以改进的。
如果你一点不懂理论,还是应该学点的。对于系统性能的设计上,算法和数据结构就像在自己手头的钱一样,它们不是万能的,但不懂是万万不行的。
怎么提高系统编程能力呢?土办法:多造轮子。就像学画画要画鸡蛋一样,不是这世界上没有人会画鸡蛋,但画鸡蛋能驯服手指,感受阴影线条和笔触。所以,自己多写点东西吧。写个编译器?渲染器?操作系统?web服务器?web浏览器?部件都一个个换成自己手写的,然后和已有的现成部件比一比,看看谁的性能好,谁的易用性好?好在哪儿?差在哪儿?为什么?
更聪明一点的办法:多拆轮子。多研究别人的代码是怎么写的。然而这个实践起来经常很难。原因:大部分工业上用的轮子可能设计上的思想和技术是好的,都设计和制造过程都很烂,里面乱成一团,让人乍一看毫无头绪,导致其对新手来说非常难拆。这种状况其实非常糟糕。所以,此办法一般只对比较简单的轮子好使,对于复杂的轮子,请量力而行。
轮子不好拆,其实是一个非常严重的问题。重复发明轮子固然是时间的浪费,但当轮子复杂而又不好拆的时候,尤其是原来造轮子的人已经不在场的时候,重新发明和建造轮子往往会成为无奈之下最好的选择。这是为什么工业界在明知道重复发明/制造轮子非常不好的情况下还在不断重复发明/制造轮子的根本原因。
程序本质是逻辑演绎的形式化表达,记载的是人类对这个世界的数字化理解。不能拆的轮子就像那一篇篇丢了曲谱的宋词一样,能读,却不能唱。
鄙人不才,正在自己研究怎么设计建造一种既好用又好拆的轮子。您没那么幸运,恐怕是等不到鄙人的技术做出来并发扬光大了。在那之前,多造轮子,多拆好拆的小轮子,应该是提高编程能力最好的办法了。
以上。嗯。
没有严格意义上的真正编程能力,只有解决问题的能力。你解决的任何问题都依赖于别人解决过的子问题,所以不存在绝对的原创还是复用。
这个问题就有点像 XKCD 某漫画,生物学家笑社会学家不纯粹,化学家笑生物学家不纯粹,物理学家笑化学家不纯粹,数学家笑物理学家不纯粹。其实大家工作在不同的维度,不能说一个维度的工作足够成功别一个维度就可以被取代了。化学足够好就能解决所有生物学问题吗?显然不是的。
你用开源项目来搭应用,你有你的上游和下游。上游是要有人去写开源项目,里面有些很多的算法是很多人长期优化的结果。没有这些人专注于细节,就没有那么好的开源项目给你直接拿来用。你的下游是更大的软件项目。你一个人的输出能写一个小应用,但在一个大应用里只能算是个功能。在公司里会有高级工程师用你写的功能来搭大应用。再下流而有公司的高管负责用多个应用来组合出公司的战略。
在这过程中,真正有趣的问题是如果你的上游不存在了,你还能解决同样的问题吗?如果你依赖的开源项目不存在,你怎么办?如果存在但 bug 很多,你会选择怎样解决?分析和解决这种问题的 meta 能力很重要。你能做好的话,换不同的具体问题你都能有一套方法解决。
作者:Cat Chen
链接:https://www.zhihu.com/question/31034164/answer/135808891
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
==============================================================================================================
前面很多答案回答的已经非常好了。
但是我想补充一些东西。
其实程序员的成长过程,也是一个自己的代码库的积累过程。
一个别人写的库,你无论使用的多么熟悉,即使源码也看懂了,但是遇到问题也是非常难以解决的。例如库里的bug,或者一些需求上的不满足需要修改。
有过多年工作经验的程序员都会发现,基本上公司越大,使用开源库越少,只是小公司为了降低开发成本才会大量使用开源软件和库。
为什么?
就是因为别人写的东西针对的都必然是一些应用场景,有其相应的权衡。而作为使用方,同样也有自己的权衡和考量,不可能完全匹配。
所以,公司越大,就越是喜欢自己写东西,中型公司会自己写各类插件或者库,稍大的公司就会自己开发各类系统、例如监控系统、上线系统、测试系统等等。再大的公司甚至连一些数据库、缓存等软件都会自己开发。再牛逼点的像google,连开发语言都会自己写,例如go和dart。
而一个程序员的能力成长路径也会非常的类似。
先是完全使用开源库,练习的是一个胶水能力。
然后开始写自己的库,要写的比开源库至少某些方面有优势,才算合格。不然全方位的弱于开源库,那写了也只是玩具。
随着自己的库的积累,开发效率会越来越高,工资水平也就越来越高。
之后,就就是锻炼自己写开源软件的能力。例如缓存系统、数据库等。
我的成长也是经历过这样的道路,我写过PHP框架和库、golang框架和库、还写过一些C++的库,还写过缓存软件等。
————————————————————————
补充一下,看到评论,有人说全部都自己开发会严重拖累开发效率。也有人说写库根本无法提升编程水平。
对此,其实,我前面的表达已经比较清楚了,在水平低的时候,如果什么都自己写确实会严重降低效率。
为什么?就因为你的水平低。还没学会走,就想着跑,当然欲速则不达。
什么时候去写基础库?到达一定层级还要提升水平,还要提高效率的时候。
去实现一套别人已经实现过的东西,在开始的时候肯定会耽误时间,但是什么叫“磨刀不误砍柴工”。自己写的东西永远会比别人的用起来顺手、改起来方便。这是为了提高以后工作效率的,除非你每半年换一门技术,没有机会积累。
而且,如果自己开发的东西能给公司同事使用,甚至开源出去给别人使用,那更是需要苛刻的优化实力和设计功力。
如果这样的场景不能提升实力和效率,那么国外那些大神们都是怎么弊出那么强大的实力的。
而且,我从来也没有说要自己写全部用到的东西。这个要看投入产出比。举个例子:
像PHP框架、Go框架这种东西,没有标准,网上一搜一大堆,那你费劲熟悉一个别人的框架,最后换个公司换个项目又要换个框架。何苦呢。
再有,像go的标准库,json解析,效率低,net/http效率低。等等都有各种各样的问题,如果想提高,为什么不自己写下呢?
相反,如果那些已经久经考验,性能、稳定性都无可挑剔的库,如果单纯为了提高开发效率,肯定是不推荐自己写的。除非想挑战下自己的开发水平。
0:可以完全理解一问题,并且给出对应的代码。
往窄了点说,这就是acm在培养的东西。
并且这不能靠调api完全解决:
有的时候,你的问题需要你把多个标准算法串一起。
比如说最近有个把STLC AST从implicit sharing变成explicit sharing的任务,这靠LCA+reverse topo dependency calculation(没这步LCA的时候scope跟着term一起被reorder了,根本做不出),最后接上metaocaml style letlist,搞定。
有的时候,根本没有任何API,需求是从一个算法改成另一个。比如说D*算法复杂度是O(nv^3)的,很不好,我们想优化下,把复杂度往下降点,这一样没有任何包可以调。
往广了说,大一点的需求也能用这种能力。既然有‘组合性’这个概念,我们就能倒过来,给出一个大型问题,分解成多个子问题,各个被单独解决后再组合一起。
名书SICP里面就很推崇这种‘理解,分解,破解’的套路,而图灵奖得主Edward Dijkstra甚至更极端,认为这方法是唯一一种编程的方法。无论这是不是唯一法,这能力都是很不可或缺的基本功。
当你掌握这方法以后,你会发现你做的很多是在脑袋中去推敲这问题的性质,试图分解这个问题,如果可以的话调用/组合已有API/算法。。是不是很像数学?因为计算机程序在某种意义上就是Mathematical Object – Curry Howard Isomorphism/Stepwise Refinement/Program Calculation都是在说这个。而当你把这套玩熟,如果你喜欢,甚至可以做到正射必中:对于给定问题,产生绝对正确的代码。这不难理解嘛,毕竟都说了是数学对象了,证明一下就好了。
=====================================================================================================================
1:能在0之上加上工程方法。
有时候这套方法不管用:比如说你跟其他人在已有code base上协作,比如说需求变更了,比如说你死活分解不出来,又比如说你根本不知道具体的需求,得慢慢探索。。其实这问题本质是,软件实在太复杂了,一个数百万行代码的项目已经超越了人类物理意义上的理解极限 – 看都看不完。
这也是为什么重头起编写一个系统很难:spec太复杂,各个组件的assumption太多,并且持续进化,不可能一口气搞定,就算给定各个预先写好的组件,也会因为assumption不match而难以组合在一起,只能通过不停的prototype,不停的重构,甚至不停的重写来加深对系统的理解。
在这之上,SICP的‘一次性理解法’已经失效,这时候就需要不精确,比起逻辑学更像生物学的技巧 – 软件工程了。
该怎么设计?
该怎么重构?
啥时候不重构而是顶着debt继续往前(不然会无限重构做不出东西来)?
该用啥技术?在各种tradeoff间如何选择?
再加上debugger/unit test/ci/git/integration test这些tool。。
这些(系统编程)
说得很好了,就不多说了。
2:对整个计算机stack有认识,能把各种技能混着耍
比如说,学过计算机体系结构,明白dennard scaling死掉后单线程已经上不去,GPU等massively parallel architecture是未来,然后给neural network迁移上GPU(deep learning)。
然后,会deep learning,发现这货给出的答案不一定是对的,但是可以当heuristic/hint,给传统方法加速(Alphago的MCTS(AI),Learn Indexed Structure中预测结果存在那(数据库),AutoTVM的快速评分(编译器),DeepCoder的降低搜索空间(Program Synthesis),Peloton的给数据库预测负载(数据库))
又或者,会FPGA,知道GPU之上还有很多优化空间,于是直接把整个matrix multiply fuse成电路(TPU),又或者会quantization,去研究怎么给quantized NN做ASIC(Bit Fusion)。
还有,会PL,发现Deep Learning的computation graph其实就是个first order PL,为了加入控制流(RNN/LSTM/TreeLSTM。。)以Lambda Cube为基础设计一个IR,再想办法在上面做反向传播,来做program optimization(TVM上的Relay)。
除了理解力到位,试图把未知的新工具用上已知领域,还有个更简单粗暴的用法:降低/消除低效接口带来的额外开销。
学了Memory Hierarchy以后,在用一个内存以前可以提前fetch,降低软件的memory access latency(prefetching)
如果有FPGA,可以把一部分任务schedule并offload上硬件,提高性能(Hardware/Software codesign)
有task要在docker里面跑?既然docker都有保护了,那还凭啥要跑一个有保护模式的OS,要多个address space并且不停在kernel/user上跑?Unikernel走起!
把这套玩到炉火纯青,还能像Midori这样,大手一挥,重新设计整个Software Stack,把里面的各种多余的抽象(protection类型系统给了,就不需要OS上搞)整合掉,爽不?
3:对不理解的CS&数学知识能在遇到的时候快速的补起来。
计算机科学实在太广太深,学习中碰到不会的东西已经是很正常了,所以说能力中还有一部分是:在代码/paper中发现完全不会的定义,如何在最短时间内学习/跳过,并不影响后续理解/debug?
而这些概念不一定只有CS的,有时候还有数学,所以还要打好最低限度的数学基础,达到‘看到不认识的数学定义不会去手足失措而是能慢慢啃/推敲’。不过还好,用到的数学跟数学系的双比不深,挺喜欢的一篇paper,Partially-Static Data as Free Extension of Algebras 也就用到了Free Algebra,属于很基础的抽象代数,并没深到那去,老板给我的paper,Sampling Can Be Faster Than Optimization ,能抓出重点,搞懂Metropolis–Hastings跟MALA(Intro to Stats就会教了,很浅),然后明白主Theorem是啥,也就差不多了,毕竟CS水这么深,主次要分清,数学能抓多少就抓多少吧。。
这些就是我所认为的不会随着时间而失效,也不能被体力劳动+调包取代的,真正的编程能力:
不停扩充自己的toolbox,并对自己的tool或多或少有本质上的理解。(Machine Learning/GPU Programming)
根据自己对这些工具的理解,想出新的组合法。(Deep Learning)
把自己的idea构建成一个复杂,大而全的系统,而不仅仅是一个玩具。(Pytorch)
落实到一个小功能的时候,能通过计算力,通过品味,设计出一个好用的API,编写一个正确高效的实现。(Reverse Mode Automatic Differentiation)
如果要用一句话概况,我猜编程能力是"对不同复杂度的问题(领域级/系统级/问题级),采用相对应工具降低复杂度,最后击破"的能力吧。
=====================================================================================================================
真正的编程能力其实简单概括就是两个词:
建模 + 实现。
建模能力如何提高关键在于 分析问题。
实现能力如何提高关键在于 工具(数据结构,算法基础,语言,框架,工具,软件)的使用。
分析问题在于抽象思维的能力。
体现在如何从表象里提取关键信息简化为可复用的pattern并选择合适的工具来实现。
而每种工具本身有自己的建模方式,所以你学习的时候还是得分析他们是如何建模的。
你现在问这个问题其实是没有掌握建模能力,无法理解或者用心去学习别人的设计。
算法的能力才算是实打实的编程能力?
算法的能力是实打实的编程能力
那"胶水"的能力和整合轮子的能力算不算编程能力呢?
"胶水"的能力和整合轮子的能力也算编程能力
所谓的编程能力到底是什么?
编程能力:针对限定的需求范围对问题进行恰当的抽象简化建模并选择合适的编程工具来实现。
我该如何提升自己的编程能力?
需要达到如下目标:
在约定的条件下,
对自己的模型和实现能够知道怎么(how),和为什么(why)的情况下,
选择最合适的方案建模并有能力实现设计来满足需求。
提高编程能力的方法:学习 + 建模 + 实现(造轮子或者仿造别人造轮子)
更多精彩回答请看:https://www.zhihu.com/question/31034164
转载请注明:学时网 » 真正的编程能力基本功