Hello World
Spiga

写程序时该追求什么,什么是次要的?

2009-05-29 15:14 by 老赵, 22750 visits

就我看来,一段程序,最该关注的是“逻辑表现”,次要的“性能”等问题的优化。当然,肯定也会有需要追求性能的场景,不过这并不是“追求”出来的,而是在大量经验累计情况下做出的正确决策。就算在那种情况下,“逻辑表现”还是非常重要的。

“逻辑表现”的意思,就是如何用程序清晰地体现你的逻辑。每个程序的目标都是解决某个特定的问题,解决问题便有思路,这个思路用程序表现出来便是逻辑。与初中高中证明数学题一样,逻辑清晰,并把它表达出来是最终的目标,而现在也只是把原本写在纸上的文字,通过代码表现出来而已。

写代码,其实也是用一种特殊的语言——程序语言,而不是文字来表达一段意思。我们平时写文章需要注意分段,分层,分条理,写程序也是一样。可能由于水平有限,你一时还无法写出华丽俊秀的文字,但是写文章的首要目标还是“清晰”,要让别人明白你的意思。写程序也是一样。在写程序时,你不应该总想着用什么技巧,追求这些技巧所带来的好处。

老赵承认,每个技巧都是有其作用的,否则就是“笑话”,谈不上“技巧”。不过有得往往就有失,某段技巧必然有其缺陷。例如在之前“数组元素交换”一题中,有朋友认为应该不使用额外变量来交换两个元素,也就是:

array[j] = array[i] + array[j];
array[i] = array[j] - array[i];
array[j] = array[j] - array[i];

他认为,这段代码节省了额外的空间,在内存紧张的情况下很有必要。但是老赵认为,这里的损失了可读性。对于一段标准的交换代码,每个人都知道它的目的,而正在读这篇文章的您,是否可以立即反应出上面三行代码的作用?

在编程领域有一个道理被广为传播:make clean code fast远比make fast code clean要容易,这里clean无疑是“清晰”的意思。因为代码清晰,我们可以找出其性能瓶颈,然后有针对性地加以优化。要知道把一个调用10000次的过程优化了20%,比调用10次的过程优化80%(假设两个过程原本消耗接近)要明显的多。

就拿那位朋友的观点,内存紧张时该怎么办。可能他的做法的确有所节省吧(不过高级语言中的“节省”,对于最终编译后的结果又是两码事)。不过在内存紧张的时候,首要做的应该还是设法探究最耗费资源的地方时什么,然后加以优化。因此,可能会对某个问题重新设计其数据结构,例如压缩数据存放方式,共享数据空间等等,而不是设法节省一个字长的内存。那么如何可以能方便瓶颈的发现呢?

清晰。

当然,上面这段代码并非不可使用,只是如果您真要这么做,请把它封装为一个子方法:

private void Swap(int[] array, int i, int j)
{
    array[j] = array[i] + array[j];
    array[i] = array[j] - array[i];
    array[j] = array[j] - array[i];
} 

一旦你把这段逻辑给分离了,在代码里只适用Swap方法了,那么程序也会一下子变得清晰起来。而且在这时候,这三行代码也变得容易理解了,别人也可以一眼看出它的作用——因为方法名已经说得很清楚了:交换。

所以,我们在写程序的时候,不如仔细想想,如何把变量名、方法名或参数名取得清楚一些,如何把程序的逻辑表现地清晰一些,如何把你的意图更好的告诉别人。

剩下的细节优化,什么内联子过程……就统统交给编译器去处理吧。

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

39 条回复

  1. 老赵
    admin
    链接

    老赵 2009-05-29 15:16:00

    看看这篇没有放到首页上的东西访问量如何。

  2. Jerry Qian
    *.*.*.*
    链接

    Jerry Qian 2009-05-29 15:48:00

    应该还行吧,沙发自己做,真过分。

  3. Joyaspx
    *.*.*.*
    链接

    Joyaspx 2009-05-29 15:54:00

    你的文章估计很多人都订阅了,所以不放在首页也是很多人都会访问的。

  4. 老赵
    admin
    链接

    老赵 2009-05-29 15:59:00

    --引用--------------------------------------------------
    Joyaspx: 你的文章估计很多人都订阅了,所以不放在首页也是很多人都会访问的。
    --------------------------------------------------------
    从结果上看,并非如此呢。

  5. Jerry Qian
    *.*.*.*
    链接

    Jerry Qian 2009-05-29 16:10:00

    其实有些道理大伙是知道了,有时候关键容易范懒。

  6. 老赵
    admin
    链接

    老赵 2009-05-29 16:12:00

    @Jerry Qian
    犯懒倒算了,不过像文章里所说的,其实是一种不正确的观念,这是问题。

  7. 我不只是糊涂[未注册用户]
    *.*.*.*
    链接

    我不只是糊涂[未注册用户] 2009-05-29 16:20:00

    说的有道理~~~~~太有道理了,我们公司存在一普遍现象,看别人代码都很难看懂,虽然公司已经严格要求编码规范这些东西了,但还是很难懂。

  8. Grove.Chu
    *.*.*.*
    链接

    Grove.Chu 2009-05-29 17:05:00

    说的很有道理啊,看来还是老赵看的比较透些。看来还是先得把事情想透了,再做这样比较好点。

  9. Kevin Dai
    *.*.*.*
    链接

    Kevin Dai 2009-05-29 17:08:00

    有时候的确要权衡一下呢。同意老赵这句话“当然,上面这段代码并非不可使用,只是如果您真要这么做,请把它封装为一个子方法:”,这样的话,自然就不会有可读性的问题了。

  10. goodddd[未注册用户]
    *.*.*.*
    链接

    goodddd[未注册用户] 2009-05-29 17:15:00

    写出让机器认识的代码和让人认识的代码还是有很大区别啊

  11. 老赵
    admin
    链接

    老赵 2009-05-29 18:00:00


    范型真的会降低性能吗? (5-29 16:41) 【博客园首页】 【.NET精华区】 (05. 实践优化) 196 93
    写程序时该追求什么,什么是次要的? (5-29 15:14) 【.NET新手区】 (03. 语言编程,05. 实践优化) 53 65

    后两个数字,前者是浏览量,后者是RSS浏览量
    果然“首页”是很重要的呢。

  12. zgzhl_1988[未注册用户]
    *.*.*.*
    链接

    zgzhl_1988[未注册用户] 2009-05-29 21:17:00

    恩说的很有道理的 清晰才是最根本的,逻辑!

  13. Anders06
    *.*.*.*
    链接

    Anders06 2009-05-29 22:14:00

    老赵你的图书推荐呢:)

  14. 老赵
    admin
    链接

    老赵 2009-05-29 22:15:00

    @Anders06
    等新版式

  15. xiongli lixiong[未注册用户…
    *.*.*.*
    链接

    xiongli lixiong[未注册用户] 2009-05-29 23:01:00

    节约内存。。。。。。

    1. 中间变量在stack分配的,何来多余的内存
    2. build后这个交换stack都不需要,用的寄存器

  16. 老赵
    admin
    链接

    老赵 2009-05-29 23:04:00

    --引用--------------------------------------------------
    xiongli lixiong: 节约内存。。。。。。

    1. 中间变量在stack分配的,何来多余的内存
    2. build后这个交换stack都不需要,用的寄存器
    --------------------------------------------------------
    哈哈。

  17. xiongli lixiong[未注册用户…
    *.*.*.*
    链接

    xiongli lixiong[未注册用户] 2009-05-29 23:14:00

    还有一个我没好意思说
    我记得clr可以打开一个编译选项, 然后如果有溢出就会自己抛异常的, 不过不太确定了
    如果真的这样, 这个lib被人家用的时候就会莫名地出异常啊。。。

  18. 老赵
    admin
    链接

    老赵 2009-05-29 23:18:00

    @xiongli lixiong
    嗯,在上一篇文章的回复里面我提过了,要写,也要加上unchecked { ... }。

  19. clayman
    *.*.*.*
    链接

    clayman 2009-05-30 04:17:00

    这也要看写什么应用,对性能敏感的程序来说,有时候是不得不把程序程序写的很丑陋,呵呵。
    另外,unchecked几乎不会带来性能提升,如果查看最终的汇编代码,CLR会在保证安全的情况下尽可能的避免检查,实在没有必要把代码标记为unchecked。

  20. 老赵
    admin
    链接

    老赵 2009-05-30 04:26:00

    @clayman
    checked和unchecked的性能可以说完全一样。
    这里我说标记unchecked不是为了性能,而是为了强制避免出现越界异常。

  21. clayman
    *.*.*.*
    链接

    clayman 2009-05-30 04:42:00

    @Jeffrey Zhao
    哦,呵呵,没有仔细看你上一篇文章:),顺便说一下,我觉得临时变量的算法要比加减法好很多,一个int并不会有什么内存压力,另外索引数组比访问临时变量慢多了

  22. 杨同学
    *.*.*.*
    链接

    杨同学 2009-05-31 12:51:00

    严重同意,代码整洁,条理清晰易读在绝大多数情况下都应该是我们的侧重点。 我也尽量想要在这方面提高自己。
    最近想学一些写文档,做presentation的技巧。想把程序的结构规划更好的写下来,让自己思路条理更清晰, 还能呈现给别人看。
    期待楼主能够写一篇这类文章。

  23. Dixin
    *.*.*.*
    链接

    Dixin 2009-05-31 21:25:00

    array[j] = array[i] + array[j];
    array[i] = array[j] - array[i];
    array[j] = array[j] - array[i];

    这本身就不对。两个int相加放到int里。

    非要这样搞,可以用异或。以前经常拿a^=b^=a^=b来开玩笑,哈哈。

  24. 老赵
    admin
    链接

    老赵 2009-05-31 21:40:00

    @Dixin
    如果允许溢出,也没什么不对的。

  25. 文超
    *.*.*.*
    链接

    文超 2009-06-03 18:25:00

    private void Swap(int[] array, int i, int j)
    {
    array[j] = array[i] + array[j];
    array[i] = array[j] - array[i];
    array[j] = array[j] - array[i];
    }

    文中将三条语句合理地导出为小函数此处相当精妙。只要函数名取好了,其义自见。这种做法非常适合简短轻快的小段代码,不宜多,多则乱,乱则惑。

  26. 张荣华
    *.*.*.*
    链接

    张荣华 2009-06-03 23:12:00

    --引用--------------------------------------------------
    Anders06: 老赵你的图书推荐呢:)
    --------------------------------------------------------


    老赵,同意你这篇文章的观点,尤其是要维护别人的代码时,如果代码思路不清晰,注释不好,维护起来真费劲。我就有过一段这样的痛苦经历。

  27. 危亭
    *.*.*.*
    链接

    危亭 2009-06-08 00:28:00

    写一个单独的方法Swap可能就不能inline了(超过了32Bytes),还不如用#region..#endregion包起来,并写上详细的注释(这段条理不清的代码是用来交换交换数值的看不懂的地方请包涵.).哈哈.

  28. Hollen Zhao
    *.*.*.*
    链接

    Hollen Zhao 2009-06-11 14:18:00

    刚学C语言时,觉得代码格式整齐、遵守命名规则,就是清晰。

    后来,觉得程序分层,模块各司其职,就是清晰。

    后来,觉得把依赖都抽象,把耦合都砍断,参数可自由配置,就是清晰。

    再后来,觉得程序调错方便,能一跟到底,就是清晰。

    现在,我也顾不了那么多了,跟着感觉走了……

  29. Nick Wang (懒人王)
    *.*.*.*
    链接

    Nick Wang (懒人王) 2009-06-21 18:20:00

    果然没放首页我就没看到

  30. 老赵
    admin
    链接

    老赵 2009-06-21 18:25:00

    @Nick Wang (懒人王)
    所以,真令人不爽啊。

  31. starfork
    *.*.*.*
    链接

    starfork 2009-07-28 18:38:00

    写着写着就忘了,变量名、方法名都随便取了,习惯很重要啊

  32. Jaypei
    *.*.*.*
    链接

    Jaypei 2009-08-07 23:26:00

    是,选择高级语言就是为了代码的可读性,可维护性这种开发层的更加先进。本身语言层面就牺牲了一些效率,如果代码里去牺牲可读性去优化效率自然是弄巧成拙。要求性能高的模块应该用效率高的语言模块化使用才对。

  33. 钢盅郭子
    *.*.*.*
    链接

    钢盅郭子 2009-08-12 10:13:00

    欲速则不达,道理是相通的

  34. skyweo
    *.*.*.*
    链接

    skyweo 2009-08-25 12:26:00

    是不是和“做事先做人”一个道理

  35. 老赵
    admin
    链接

    老赵 2009-08-25 12:34:00

    @skyweo
    这我就不知道了……

  36. 似水之心
    *.*.*.*
    链接

    似水之心 2009-08-31 15:35:00

    刚学C语言时,觉得代码格式整齐、遵守命名规则,就是清晰。

    后来,觉得程序分层,模块各司其职,就是清晰。

    -----------------我到这儿了

  37. Hawkon
    *.*.*.*
    链接

    Hawkon 2009-09-05 17:57:00

    能否从整体把握程序决定一个程序员甚至是一个人的高度。

  38. 司徒正美
    *.*.*.*
    链接

    司徒正美 2009-09-08 17:16:00

    说得没有错!

  39. llltgd
    117.11.234.*
    链接

    llltgd 2011-02-04 11:38:25

    学C到现在一直以为交换应该写成

    #define swap(a,b) ((a)^=(b)^=(a)^=(b))
    

    那个+总认为会越界……

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我