programming

程序员修炼之道

每年寒假总是要读完一本书,去年寒假读的是白帽子讲 Web 安全.今年读的是程序员修炼之道:从小工到专家.去年那时候估计没有搭建好博客. 所以没有写点什么,今年还是写一下读完的感受吧.

这本书说实话我大一就买了,但是那时候没有一点项目经验.读起来宛如天书,没读一半就放弃了.因为那个看起来不知所云的目录让我顿时失去了兴趣. 大一的我确实无知,自负,浮躁,没有认真读下去.当时就沉迷于学习各种编程语言...没有审视到这本书的价值.如今我已大四,也算有了不少项目编程 经验,写过上万行代码,对于这本书的感受就是爱不释手.这本书用非常幽默的方式简述很多朴素而真挚,简单而有效的编程哲学.

全书基本是经验之谈,经验往往不能告诉我什么一定对,但是可以告诉我们什么一定不对.在提示 36 中讲述了需求之坑.告诉我们一个注重实效的程序员 应该需要挖掘需求而不是搜集它们.确实,客户往往不能很好的阐述需求,需要程序员进行深入挖掘才能使项目顺利进行.文中也举了很简单的例子进行阐述. 我一开始做项目的时候很顽固的要求需要明确的需求文档,现在想想这完全就是个笑话.因为客户自己都不清楚到底要做成什么样子.

本书的涉猎范围很广,也有很多专业名词.每个章节都可以延展出很多内容.我不认为这是一本只需读一遍的书.在每个编程阶段去读这本书都会有不同的感受. 这本书我肯定会珍藏,以后也会反复读的.这是一本程序员必须读的书,希望大家可以耐心的读一下这本书.

这本书的精彩之处只可意会不可言传,本人文采不好,但是极力推荐.最后摘抄下这本书阐述的注重实效的程序员的特征:

  • 早期的采纳者/快速的改编者. 你具有技术和技巧上的直觉,你喜欢试验各种事物.给你一样新东西,你很快能把握它,并把它与你的知识的其余部分结合在一起.你的自信出自经验.
  • 好奇. 你喜欢提问.那很漂亮--你是怎么做的? 你用那个库时有问题吗?我听说的这个 BeOS 是什么? 符号链接是怎样实现的?你是收集小知识的林鼠(pack rat),每一条小知识都可能会影响今后几年里的某项决策.
  • 批判的思考者. 你不会不首先抓住事实而照搬别人的说法.当同事说"因为就该那么做"或者供应商允诺为你的全部问题提供解决方案时,你就会嗅到挑战的气息.
  • 有现实感. 你会设法理解你面临的每个问题的内在本质.这样的现实主义给了你良好的感知能力:事情有多困难,需要多长时间? 让你自己了解某个过程会有困难,或是要用一点时间才能完成,能够给予你坚持不懈的毅力.
  • 多才多艺. 你尽力熟悉广泛的技术和环境,并且努力工作,以与各种新发展并肩前行.尽管你现在的工作也许只要求你成为某方面的专才,你却总是能够转向新的领域和新的挑战.

最后,你是一个注重实效的程序员吗?

关于程序运行效率的一点见解

最近在看计算机组成原理等 4 本考研书籍...发现其实回过头来看这些当年没认真学的书的话还是有不少收获的. 当年小白的我只知道编程看语言类的书籍,无法理解数据结构,计算机组成原理一类书对编程的影响. 刚开始学这本书的时候就感觉我一个搞软件编程的看硬件干嘛.又是指令又是寄存器啥的,还有那可怕的电路图... 看的真头大啊,不过其实想想,这门课要是真没用学校也不会开考研也不会考啊!当年真是图样图森破啊...

大一的时候一直以为搞软件的就要多学点编程语言...我现在倒是学了不少.到头来没一个拿的出手的,还真是可悲, 广度是有了但是没深度,同样的语言人家编程风格和效率就是比你高.这个是在什么 C#本质论 C++ primer 里面学不到的东西,说深了就是编程素养.是不断积累的,不是看一本书就能迅速提升的东西.只上过培训班的程序员永远也不可能明白学习计算机组成原理会对编程有更深入的了解.最终写的代码也只是知其然而不知其所以然.

编程素养是通过各方面计算机知识的综合培养才能得到提高的.接下来我就举个例子来谈谈学过组成原理对编程的理解和 提高.例子很简单,就是关于数组的遍历.一个二维数组,假定是 10*10 的吧.那么遍历输出的话,大家肯定都会的,很简单.

for(i=0;i<10;i++)
  for(j=0;j<10;j++)
      printf(a[i][j]);
1
2
3

这个是很简单的两层 for 循环,我写的是伪代码,变量定义输出什么的就不要吐槽了.学过数据结构的同学应该都明白虽然二维 数组都用矩阵来表示,但是实际储存的时候一般都是按照行优先的方式储存的.我的这个版本是先行遍历再列遍历的.

for(j=0;j<10;j++)
    for(i=0;i<10;i++)
        printf(a[i][j]);
1
2
3

那么先列遍历再行遍历呢?大家一般都会觉得这个其实没什么差别吧.用数据结构的思想来说,空间复杂度和时间复 杂度都是一样的,运行起来没啥区别的.当然对于现在的高速 CPU 和这个元素比较少的数组来说其实也没多大差别,但是不能 因为这个就去忽略程序的效率问题.

为了讲述这个问题,首先得了解计算机的基本运行原理.首先程序是放在内存中的,但是执行程序需要用 CPU 来计算.因为 CPU 运行 速度极快,但是内存速度却没有显著的提高.所以引入了高速缓存也就是 Cache,首先把内存中的程序调入高速缓存中.然后 CPU 再从 Cache 中取出数据进行运算.但是因为 Cache 的容量极小,但是内存很大,根据程序访问的局部性原理.指令通常是顺序存放, 顺序执行的,数据一般也是聚集的存储在一起的.所以调入 Cache 中的数据基本上会是一段时间内 CPU 欲调用的数据.

当 CPU 发出度数据的请求时,如果访问的地址在 Cache 中即命中,那么直接从 Cache 中读取数据,和主存无关;如果 Cache 不命中, 则仍需访问主存,并把此地址所在的块一次性都调入 Cache 内.

显然,如果命中率高的话,那么程序直接的速度就更快.对比行遍历和列遍历.发现行遍历每次都是按顺序取数据的,因为是按照行优先方式 进行存储的嘛,所以命中率必然高.而列遍历的话每次取的都不是相邻的数据,会导致命中率大大降低,运行效率低.所以一会写程序的话 遇到数组遍历的话肯定要使用行遍历更好.

类似的问题还有 GOTO 语句,大一的时候老师明令禁止不准在程序中使用 GOTO 语句,但是就是不说为什么...学过计算机组成原理也就 明白了,因为 GOTO 语句违反了程序顺序执行的原则,使程序运行效率不高,而且程序读起来也很麻烦.

到此,对编程效率的见解就结束了,如果有什么纰漏的地方,还请大家指正,感谢您能读到最后~