设为首页收藏本站

数码鹭岛论坛

 找回密码
 注-册

QQ登录

只需一步,快速开始

搜索
查看: 6203|回复: 16
打印 上一主题 下一主题

编程珠玑番外篇

[复制链接]
跳转到指定楼层
1#
发表于 2010-5-24 15:12:46 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
编程珠玑番外篇-1.Plan 9 的八卦
原文:http://blog.youxu.info/2008/10/06/about-plan/

在 Windows 下喜欢用 FTP 的同学抱怨 Linux 下面没有如 LeapFTP 那样的方便的工具. 在苹果下面用惯了 [url=http://cyberduck.ch/]Cyberduck 的同学可能也会抱怨 Linux 下面使用 FTP 和 SFTP 是一件麻烦的事情. 其实一点都不麻烦, 因为在 LINUX 系统上压根就不需要用 FTP. 为什么呢? 因为一行简单的配置之后, 你就可以像使用本机文件一样使用远程的任何文件. 无论是想编辑, 查看还是删除重命名, 都和本机文件一样的用. 这么神奇的功能到底如何使用呢, 待我一一道来.
首先, 如果你的内核版本是 2.6.14 或者更新(uname -r 可以查看你的内核版本), 你的机器上的已经有了这个支持了. 在这种情况下, 你可以使用:
sudo modprobe fuse
激活内核模块. 然后, 比如说, 你想SSH远程 remote.com 上的 /dir 目录, 作为本机的 ldir 目录, 你可以使用:
sshfs name@remote.com:/dir /ldir
然后, 您就可以完全在 ldir 目录下操作任何的远程机器上的文件了. 是不是很简单
如果要使用 ftp, 我们要使用一个叫做 curlftpfs 的开源软件. 幸运的是, 你可以直接使用 apt-get install curlftpfs 直接得到他.
安装结束后, 我们依然使用
curlftpfs ftp://user:pass@ftp.remote.com/dir ldir/
就可以把远程的目录当成本地的目录一样使用的. 拷贝啊重命名啊都可以. (当远程的内容直接能被你访问的时候, 谁拷贝啊
说到这里, 聪明的读者肯定要说: 靠, 远程的文件都能当成本机的用, 那太好了, 整个互联网就是我的磁盘嘛. 你还真说对了, 对于一个用户来说, 协议是不重要的, 重要的是数据. Gmail 不是有N个Gb的大小么, 让我们直接把 gmail 当磁盘好了. 于是有了 [url=http://richard.jones.name/google-hacks/gmail-filesystem/gmail-filesystem.html]Gmail FS. 这样, 你可以给你的邮件标题做支持正则表达式的查找噢
除此, 还有 [url=http://wikipediafs.sourceforge.net/]wikipediaFS, 可以用自己喜欢的编辑器直接编辑维基百科的文章. 还有 [url=http://manishrjain.googlepages.com/flickrfs]flickrFS. 直接用自己喜欢的编辑器可以编辑图像元信息, 还能 chmod 754 让图像只能被朋友访问噢 :)
以上的这些酷实现, 都是 [url=http://fuse.sourceforge.net/]FUSE 这个内核模块作为基础支持的. 那么, 这个牛逼的网络也是文件的点子是哪个牛逼的人想出来的呢? 答案是 UNIX 的发源地, 贝尔实验室. 确切的说, 是在设计 UNIX 的继承者– [url=http://en.wikipedia.org/wiki/Plan_9_from_Bell_Labs]Plan 9 的过程中提出来的. (小八卦: UTF-8, 就是世界上现在最通用的字符编码体系, 就是 Plan 9 操作系统的一个副产品).
一切都是文件这个点子倒不是什么新的点子, 在 The Unix Programming Environment 这本圣经里面就旗帜鲜明的打出 “UNIX下一切都是文件”的旗号. 但是毕竟旗号是旗号, 现实是现实. 关于文件的抽象在其后的发展中发现了这样那样的局限, 于是恶名远扬的 ioctl 函数被引进系统. 所以口号是口号, 实现是实现. 显然当初设计 UNIX 的几个哥们很不满意, 所以搞了一个 Plan 9 (搞 Plan 9 的人就是写 The Unix Programming Environment 的人). 在 Plan 9 系统中, 一切都是文件, 显卡是文件, 内存是文件, 进程是文件, 网络是文件…
等等, 熟悉 Linux 的用户要说了, 在 Linux 下面, 进程的确是文件啊. ls /proc 就能看到当前所有的进程, 不也是文件么. 是呀, Linux 这个就是和 Plan 9 学来的. 但是 Linux 学得不彻底, 因为这个文件系统的访问接口并不完整: 你不能通过 rm 一个文件杀死一个进程. 也不能通过 cp 拷贝出一个进程. 而在 Plan 9 上, 你不光能通过普通的文件操作命令去控制进程, 你还能 mv 一个进程. 我们刚才说了, 进程就是文件, 还能把其他机器上的文件当成本机一样用. 屏幕前聪明的你肯定一下子悟到了: 一定这居然是一个分布式的操作系统啊! 在这个系统里, 进程可以被其他计算机看到, 并且控制. 而管道可以横跨不同机器的不同进程, 这简直是把单机的概念都给抹杀了, 简直就是”网络就是计算机啊”.
熟悉互联网的读者都知道最近炒得很热的云计算啥的, 对用户无非就是互联网作为硬盘, 对公司无非就是分布式的操作系统协同工作, 在屏幕前的您肯定如我一样, 一拍大腿说: 靠, 贝尔实验室养得不是计算机科学家, 而是一大批未来学家啊. 30多年前人家搞互联网, 而今我们用互联网. 20年前人家用GUI, 如今我们用GUI. 10年前人家搞云计算, 而今我们炒概念闲扯淡. 差距啊.
说到这里就到了今天技术八卦的尾声了: 当年设计 Plan 9 的牛人 bwk 和 Rob Pike 等哪去了?
bwk (别查了, 就是那个写 C语言书的, 写AWK的) 年纪大了去普林教书了(人家本科生能不牛逼么). 而 Rob Pike 年轻, 风华正茂正当时. 您想,对于那些做云计算的大公司来讲, 这么牛逼的人哪能落入对手手中? 您这么想的, Google 也是这么想的. Rob 同学现在在 Google 研究院很爽的做着主力工程师呢. (小八卦, 他是Google 中唯一享有单字母邮箱的: r@google.com).
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享
2#
 楼主| 发表于 2010-5-24 15:15:44 | 只看该作者

编程珠玑番外篇-2.计算机图书排版的八卦

原文: [url=http://blog.youxu.info/2008/10/13/trivia-about-computer-typesetting/]http://blog.youxu.info/2008/10/13/trivia-about-computer-typesetting/

大家都知道, 计算机科学家超级爱动手自己开发工具, 而且对美有超乎常人的需求. Knuth 爷爷当年觉得自己辛辛苦苦的好书被排版成地摊上的厕纸一样, 一怒之下自己搞出了红遍大江南北的 TeX. 从此整个世界都清净了. 排版是计算机科学家研究的一个很好玩的领域, 这篇文章就谈谈我所知道的关于排版的八卦.

先说 Knuth 爷爷的吧. 首先, 是在设计 TeX 的过程中, 这位老爷爷研究了很多著名的字体, 成了名动一时的字体专家, 据说和乔布斯并称为加州最懂字体设计的两个搞IT的 (我瞎说的). 研究字体之余, 他就研究收集各大书法家的作品, 然后这位老爷爷又是一个基督教徒, 所以干脆用它的收藏出了一本书, 叫做 <3:16>. 这本书特别牛逼, 是一本用计算机科学研究上帝存在的. 而且发挥计算机科学的小幽默, 取圣经每章的第3节第16小句, 还证明了这个和随机一样好.

还是克爷爷, 写完TeX之后不过瘾, 要写本书来冲冲喜, 于是写出了极其牛B名字的 The TeXbook. 一语双关, 表现了牛人一贯的狂妄. 写完这个他又想写写自己的字体和绘图系统设计(metafont 系统) 所以干脆出了五卷书, 行话称作ABCDE, 也是用名字来表明: 看, 基本的入门书, 你非看不可.

跑题一下: Knuth 爷爷最喜欢让人家看到他提出的名字就腿发软. 比如他提出了一个叫做 Literate Programming 的东西, 并且很不怀好意的对 Dijkstra 说, 小样, 当年你说 structured programming 的时候我非要用 goto, 结果人家都说我是 unstructured programming (没结构的编程), 现在我要提出一个叫做 literate programming 的东西, 你要是不跟着我混, 人家就会叫你 illiterate programming (没文化的编程). 在这么邪恶的名字下, 全世界程序员只好个个听这个老头的话, 乖乖的使用文档和程序融为一体的”有文化的”编程习惯.

其实克爷爷属于斯坦福家族的. 在70-80年代, 世界上还有一个NB的研究机构: 贝尔实验室. 贝尔实验室自己也开发了自己的排版工具: Troff. 开发者是著名的K, 就是 K&R 里面的那个K. 这个 Troff 也是一个牛到极点的排版软件, 比如说, 当年那些科学家都对出版社的排版不满意, 所以都威胁出版社说: 我自己来排版, 你们只管印刷就行了. 就是因为这帮科学家开了这个传统, 所以后来出版商遇到想自己排版的, 都用巨崇拜的眼光打量着你.

说到 troff, 以下大名鼎鼎的书都是用 troff 排版的:

Advanced Programming in the UNIX Environment
The AWK Programming Language
The C Programming Language
Compilers: Principles, Techniques, and Tools
Computer Networks, 3rd Ed.
Computer Networks And Internets, 3rd Ed.
The Design and Implementation of the 4.4BSD Operating System
Effective TCP/IP Programming
The Elements of Programming Style, 2nd Ed.
Internetworking With TCP/IP Volume 123
More Programming Pearls
The Practice of Programming
Software Tools
Unix Network Programming
The UNIX Programming Environment
Programming in C++

所以说, troff 排版的无烂书. 当然, TeX 家族也不是吃素的, SICP, TAoCP, CLRS 都是用 TeX 搞出来的. 陶哲轩也说, 鉴别民科文章第一步就是看是不是用TeX排版的. 可见排版排得专业, 也是好文章的一个先决条件.

我觉得可以把以上的结论概括成 徐氏排版定理, 如果一本书, 不是以上所说两个软件排版的, 又不是 O’Relly 出版的, 那是好书的概率也就不怎么大了. 作为一个作者来讲, 一定要记得用 troff 或者 latex 排版

troff 和 latex 都是一脉相承的, 理念也差不多, 所以牛B的开发人员两头都在玩, 比如一个叫做 Werner Lemberg 的牛人, 就是 troff 的开发人员, 同时还跑到 TeX 那里开发了支持中日韩的 CJK 包. (大家都知道, 软件的中文支持从来都不是中国人开发的)

史上最牛的程序员 Bill Joy 同学据说用了一个周末就写出了 vi, 所以大家都怀疑, 他用了半个小时的时间写了 BSD 上的 troff. 他写的这个程序, 被SUN用着, 一直用到今天.

最后强行插播一条广告: 我最近要写一本小册子, 叫做 Motifs in Computer Science (原名叫 Meta Ideas in Computer Science). 一定保证用 LaTeX+Troff+reStructuredText 排版, 按照我的 Troff/Latex 排版无烂书结论, 这本书也不是太烂. 欢迎捧场.

再补充一则八卦: 话说当年 PDP-11小型机特别贵, 但是贝尔实验室的科学家又想要用. 怎么办呢? 于是, 他们发挥了科学家爱忽悠的能力, 去和经理说: 你看, 我们文档的排版很烂吧(当年还是打字机时代), 你们投资一下搞一个小型机回来, 我们保证给你们开发一个在这个机器上用的文档排版系统. 经理一听, 大笔一挥说: 买之!. 科学家一听都乐了, 哈哈, 我们有新玩具了. 然后, 他们就开始在 PDP11 上开发 UNIX 了. 经理也不懂, 看他们搞的好玩, 就不时来问问: 老大们, 排版系统怎么样了? 贝尔的科学家一边敷衍敷衍, 一边继续搞 UNIX 和 C 语言. 等这两样都搞好了, 瞬间就写了一个排版软件, 就是 nroff. 经理可乐了, 说, 哎, 我们终于投资有回报了啊. 科学家也乐了, 因为若干年之后, C 和 UNIX 红遍大江南北, 因此两人拿下图灵奖. 所以说, 做研究这东西, 一定要先把基金忽悠过来, 然后想干啥干啥, 最后结果反而超出预料. (贝尔实验室的人居然研究宇宙背景辐射拿诺贝尔奖, 这种宽松宽容的基础研究在其他地方是很难遇到的).
3#
 楼主| 发表于 2010-5-24 15:17:21 | 只看该作者

编程珠玑番外篇3 — 关于程序优化的八卦

原文: http://blog.youxu.info/2008/11/13/anecdotes-about-program-optimization/

(把八卦和文章重新分了一下类, 准备围绕自己的心得写个编程珠玑番外篇, 这个算第三篇吧)

<代码大全> (Code Complete) 是一本很好的书. 我建议像我这样写的程序总行数不超过50万的程序员应该买一本放在案头 (当然<代码大全> 不如 <Software Tools> 这本书好, 这个我以后有机会写文章细谈) 如果你天天编程序, 我建议你买一本. 如果你已经有了<代码大全>, 我诚心建议你赶快翻开此书, 撕去第26章. 因为代码大全的其他章节可以让你成为优秀的程序员, 唯独第26章, 读了之后立即从优秀程序员变成最差的程序员.

为啥? 因为第26章讲的, 都是怎么调节代码使得代码跑得更加快的技巧, 而这些技巧, 几乎都是让一个好程序变成差程序的技巧, 是教你不管三七二十一先对程序局部优化的技巧. 而局部优化是让程序变得糟糕的最主要的一个原因. 用高爷爷的话说, 提前优化是万恶之源 (Premature optimization is the root of all evil). 这些技巧, 就是带你去万恶之源的捷径.

代码优化究竟是什么洪水猛兽, 又究竟有多少伟大的程序员因为代码优化声名扫地, 请看本期关于代码优化的八卦.

话说当年在贝尔实验室. 一群工程师围着一个巨慢无比的小型机发呆. 为啥呢, 因为他们觉得这个机器太慢了. 什么超频, 液氮等技术都用了, 这个小型机还是比不上实验室新买的一台桌上计算机. 这些家伙很不爽, 于是准备去优化这个机器上的操作系统. 他们也不管三七二十一, 就去看究竟那个进程占用CPU时间最长, 然后就集中优化这个进程. 他们希望这样把每个程序都优化到特别高效, 机器就相对快了. 于是, 他们终于捕捉到一个平时居然占50% CPU 的进程, 而且这个进程只有大约20K的代码. 他们高兴死了, 立即挽起袖子敲键盘, 愣是把一个20K的C语言变成了快5倍的汇编. 这时候他们把此进程放到机器上这么一实验, 发现居然整体效率没变化. 百思不得其解的情况下他们去请教其他牛人. 那个牛人就说了一句话: 你们优化的进程, 叫做 System Idle.

所以说. 优化这东西, 一定要有一个全局的思路, 否则就是纯粹的无用功, 有时候还是负功. 在<编程珠玑 II> 第一章, Jon Bentley 就着重提醒了代码 profiling 的重要性. 说到 profiling 这个词, 就不能不再次提到万众敬仰的高爷爷. 高爷爷在1970年的暑假, 通过捡Stanford 大学机房扔出来的垃圾(其实是含有程序的磁带), 写出了一篇震古烁今的论文 “An empirical study of FORTRAN programs” (FORTRAN 程序的实证分析). 除了抱怨写程序的人不看他的 TAoCP 之外(因为一个程序用了被高爷爷定性为史上最差的随机数发生器算法, 有兴趣的可阅读 TAoCP vol2), 这篇论文主要说了三个划时代的东西:

1. 对程序进行 profile 是每个编程系统的居家旅行必备.

2. 在没 IO 操作的情况下, 一个程序中 4% 的代码占用了超过50% 的运行时间.

3. 97% 的情况下对程序进行提前优化是万恶之源.

这三个道理, 用大白话说, 就是: 1 程序都存在热点, 有优化的空间. 2. 但是97%的情况下程序员优化的都是错的地方, 反而把程序优化糟了. 3. 想要做优化, 第一步就要先知道程序在什么地方耗时间而不是靠猜.

说到热点, 顺带拐八卦一下Java的速度. Java 1.5 的虚拟机的关键技术, 就是叫做 Hotspot (热点). 传统上, 大家都认为 Java 比C 要慢. 其实不然. Jython 的作者 Jim Hugunin 就曾经说过, 其实两者差别不大 (http://hugunin.net/story_of_jython.html). 也有一些其他的测评说, Java 比 C 要快. 原因就在于, Java 虚拟机能够找到热点, 对热点专门做优化. 而C程序编译好了, 即使有热点, 也只能靠CPU去优化了. Java 的优化比 CPU 要深且更全局.

言归正传. 关于 FORTRAN 的 profile 的传统被继承了下来, 基本上现在任何的过程式主流编程语言都支持 profiling 工具. 关于 profile 怎么做的问题, 等我有空了好好写文章介绍. (因为我发现, 除了编程珠玑, 没有一本书提到过).

做程序优化的八卦就太多了, 说一个Beautiful Code 上的吧. 话说世界上做线性代数的库叫做BLAS, 基本上是工业标准. 因为线性代数运算太重要了, 所以各大处理器厂商都有 BLAS 的实现. Intel 的叫 MKL, AMD 的叫 ACML. 矩阵乘法实现的好坏, 直接决定了处理器的性能测试的分数(因为现代测处理器的速度的程序, 比如LAPACK指数, 基本上都是用 BLAS 里的矩阵乘法做基准). 去年 nVIDIA 高调宣传自己的 CUDA 系统比CPU厂商快10倍到100倍, 借此打开了GPU计算的大门(令人发指的达到500GFlops, Intel 最新的只有50GFlops). 其中 CUDA 可以理解为是 BLAS 在 nVIDIA 平台上的实现. 自从nVidia 推出 CUDA 以后, 俨然不把 intel 这些厂商放在眼里, 心想, 小样, 你们还是做通用处理器吧, 浮点乘法这些高级的东西, 还是放在显卡上比较好. nVidia 和 IBM/SONY 的阴谋很不小呢. 要是浮点计算比 Intel 快这么一两个数量级, 以后世界上前五百名超级计算机就全部变成什么用光纤网连起来的 PS3 机群, nVidia 显卡机群之类的. 人家外行见了计算机科学家肯定要问: 你们搞高性能计算机究竟是搞计算还是打网络游戏啊??

还是言归正传(我怎么老走题?), 简要的说一下 BLAS 优化. 单处理器上对 BLAS 的优化主要体现在对 cache 的高效使用. 矩阵乘法中, 如果矩阵都是按照行存储, 则在A*B中, 对B的访问是按列的. 假设B一行有N个元素, 那么在存储器中, 两个同列不同行的元素所在的存储单元相差N. 因此, 对B的访问并不是局部化的. 因为访问不局部化, 所以每次乘法, 都需要从内存中调一个 cache 单元到CPU. 这个极大的降低了处理器的执行速度. 因此, 矩阵乘法的优化的核心, 在于局部化B的访问. 反过来, 如果矩阵按照列存储, 则要局部化对A的访问. 关于怎样局部化访问还能获得正确的乘法, Beautiful Code 一书的第14章有非常好的讲解, 我就不废话了. 总之, 矩阵乘法局部化的好坏, 取决于一个机器的 cache 的大小.

多处理器和向量处理器就又不一样了. 要想利用好, 就要把计算任务平均的独立的分到不同的处理器上. 所以, 在这里, 优化就变成了分解成若干的小问题. 因此, 分治算法成了主流. 具体矩阵乘法怎么分块, 大学数学都讲了, 我就不废话了. 事实上, 之所以 nVidia 能和 Intel 干, 就是因为显卡上令人发指的有 64 个计算核心, 而 Intel 最牛X的才 4个. 那为啥 Intel 自己不多做几个核心呢? 因为 Intel 自己把自己带沟里去了 — Intel 处理器太复杂支持的功能太多了, 一块硅片上根本放不下很多核心. 而 nVidia 一直就是专用处理器, 每个核心功能简单, 可以做到很小.

Beautiful Code 第14章就是讲了随着计算机体系结构的变化, BLAS 是怎么进化的. Tanenbaum 曾经说过, 随着一个科技的出现, 某个 idea 可能就销声匿迹了. 但是说不定下一波科技再来的时候, 这个 idea 又复活了. BLAS 从串行, 到向量, 再到串行(带cache的RISC), 再到向量(Cell), 就是一个绝好的例子. 对这个进化史感兴趣的读者不可错过这一章妙文.
4#
 楼主| 发表于 2010-5-24 15:19:32 | 只看该作者

编程珠玑番外篇-4. Linux 下的 Facade 程序

原文: [url=http://blog.youxu.info/2008/11/17/facade-commands-in-linux/]http://blog.youxu.info/2008/11/17/facade-commands-in-linux/

Linux 下的命令行工具大致有两个流派, 一是以小而精见长的, 只能提供一个简单的小功能. 比如 yes 这个命令, 除了输出一大串永不停止的 y 之外毫无用处. 这个工具看上去土, 很没用处的样子. 碰到要你一路回车法的时候, 这个工具就大大的有用. 所以我每次帮人使用一路回车法装 windows 的时候, 就怀恋 Linux 下的这个 yes. 过一个管道, 就省去了在电脑面前按下几百次 y 的繁复工作.

还有一种工具, 是我今天要说的重点. 这种工具一般是一个简单的命令行调用, 却有着几十种甚至上百种不同的参数的组合, 用这些参数能搭配出谁也没用过的功能. 以 gcc 为例, 居然有两百多个不同的命令行参数, 范围涉及到程序编译, 连接设置, 库设置, 优化, 报错信息, 调试信息等等, 任何一个正常的人想要穷尽学完这些参数都是不可能的. 同样的库还有 convert (图像转换的), ffmpeg (视频处理的), curl (内容抓取的). 看上去这些参数指示的功能乱七八糟的堆砌在一起的样子, 仔细一想这些功能的确是相互关联的, 所以被放到了一个工具之下. 这些工具和上面的工具的哲学是反其道而行之的: 集一大类功能于一个工具, 任何类似的操作都能通过这个一个命令+不同的参数来完成, 而非”do one thing, do it well”. 这些工具和传统意义上的 UNIX 工具哲学是不大像的. 为了区分他们, 我把它们叫做 Facade 工具, 因为这些工具的设计哲学很类似于 Design Pattern 里面的 [url=http://en.wikipedia.org/wiki/Facade_pattern]Facade Pattern (Facade 模式的核型是用一个统一的接口管理对一个系统的访问. 比如 gcc 就是对整个编译系统的接口, ffmpeg 就是对整个视频处理系统的接口, display 就是对整个 X 显示系统的接口等等.)

之所以区分这两者, 是我体会到: 在具体的学习过程中, 对付两者的学习方法是截然不一样的. 学习小工具, 基本上就是学一个简单的名字到功能的定义, 加一些简单的参数. 除了名字比较别扭外, 使用很方便, 学习曲线不陡峭. 学习的要点不在于这些小工具本身, 而在于利用管道和其他工具通信(小工具从来就不是单独使用的, 比如 yes, 比如 tr, 我几乎没见过不用管道的情况下用他们的); 和上面相反的是, 我几乎没见着 Facade 工具用在管道里面的.

原因是 Facade 工具基本上是一个自成体系的完整的操作方式, 就像一个新的领域的一种新的”语言”一样. 因此, 不掌握一点基本的编译知识, 就不可能把 gcc 玩转, 因为那些参数的含义的理解, 都是需要相应知识的. 我也常常看到不少 做 Web 程序的哥们对 curl 的每个边边角角都很熟悉, 但是对 gcc 不太熟, 这也是很正常的, 因为 Facade 程序本来就是属于面向一个特定领域的工具.

我在学习这两种截然不同的工具的时候也曾感到过困惑: 怎么有的程序这么多参数, 全学会怎么可能. 在浪费了不少时间乱看这些 Facade 程序的 man 文件之后, 我认识到: 除非我写操作系统, 要让我的程序编译的时候有几百个参数, 否则, 简简单单的用 gcc 常用参数就能解决99%的问题了. 我觉得, Facade 程序的要点正是在于, 用一些简单的参数组合(更多情况下其实不要参数) 就可以完成 90% 的常用例子. 至于剩下的 10%, 遇到了再去查文档就行了. 同时, 对于不在自己”常用工具集”中的一些 Facade 工具, 认真学习他们的用法是一件非常耗时且几乎没有任何收获的事情, 而且学到的也不会被实际用到. 所以, 千万不要被”获取新知识的成就感” 给蒙蔽了, 去钻研那些琐碎的边边角角.
而对于小工具, 却要反过来. 我觉得在学习小工具 (尤其是 [url=http://en.wikipedia.org/wiki/GNU_Core_Utilities]coreutils 里面的所有命令) 的时候, 最好要做个有心人, 把大部分参数弄清楚记住 (本来参数也不多). Linux 下的小工具基本上是千锤百炼经过无数进化的, 应该说每个选项都是很常用的. 搞明白这些选项, 可以极大化发挥这些小工具的优势, 还能提高自己的生产率. 举个例子: 比如说 ssh 这个程序, 90% 的哥们就是用他来登录服务器, 然后运行服务器上的某个程序. 其实 ssh 的文档写得很清楚, 你可以把 ssh 后面接一个命令文件. 比如说
ssh name@server.com ls
就可以直接显示服务器上的目录了. 还可以拓展一下,
ssh name@server.com < script.py
就可以直接把本机上的 script.py 放在服务器上跑, 无需把文件先拷贝过去. (走题一下: 跨平台的脚本语言的好处就在这里. Apache 的 Hadoop 是 MapReduce 的一个开源实现, 他的任务控制器就是采用我说的这种方式来调用各个机器上的Mapper 或者 Reducer 工作的). 因此, 掌握 ssh 的加命令的用法, 在我看来, 是值得的.

很多小工具都有这样不太鲜为人知的用法, 熟稔这些用法, 我觉得是值得的, 况且这也不需要花多少时间, 只要打印一份文档每天睡前看半页就行了.  我以前还有整理了不少这类平时大多数人注意不到的小命令的一些”黑魔法”. 我觉得这些黑魔法一点都不是什么奇技淫巧, 而是实实在在能提高效率的魔法, 是居家旅行必备的工具套装.

PS: 最近有几个朋友看了我的博客, 发信让我推荐学习 Linux 的书. 我推荐 “鸟哥的Linux私房菜” 这本书. 我学 Linux 的过程中没看过这本书, 所以折腾的比较曲折. 直到我大四我才看到这本书, 这本书是一本非常深入浅出的好书.

PS2: GNU 的工具链有把小工具 Facade 化的倾向. 连 ls 这么简单的命令都有几十个参数. 在这种情况下, 还是挑选一些认为会常用的参数学习一下就行了, 没有必要去追求高大全. 一般说来, 这种两个字母的小工具, 如果后面加的参数超过6个字母, 就完全不对味了. 工具这东西, 强极则无用至极.

-EOF-
5#
 楼主| 发表于 2010-5-24 15:21:35 | 只看该作者

编程珠玑番外篇-5.比代码大全好的两本书A

原文: [url=http://blog.youxu.info/2008/11/23/software-tool/]http://blog.youxu.info/2008/11/23/software-tool/

上次我说到”[url=http://blog.youxu.info/2008/11/13/anecdotes-about-program-optimization/]比代码大全好的书“, 第一本指的是 <Software Tools>. 为了说这本书的优点, 得先说这本书的缺点.

这么书基本上绝版了. 而且也没有中文版. Amazon 连旧书摊总共就不到50本. 可见这本书目前不是一本让广大程序员喜闻乐见的书. 其次, 这本书用的说明问题的语言叫做 Ratfor, 基本上是 FORTRAN 和 C 杂交的产物. 估计全世界用这个的程序员和现存的这本书的数量差不多多. 但是你要是认为这是一本古董书, 烂书或者非畅销书, 那你就错了.  因为是一本编程书籍, 生命周期本来就短, 因此单以现在的销量判断好坏, 并不科学. 江湖失传已久的如来神掌送给周星星的时候, 周星星也不以为然. 但最后威力无穷. 希望这篇书评, 能够让读者信服这是一本如来神掌的秘籍.

这本书的作者是 Brian W. Kernighan 和 P. J. Plauger . 关于这两个作者出书质量好的废话我就不多说了(不知道没听说第一个的回家用C写一个Hello, world 并面壁). 先说这本书讲的什么吧.

这本书主要两条线, 一条是怎样通过一个叫做 Ratfar 的语言, 一步一步构建 UNIX 系统下的 cat, wc, tr, sort, tar 等等这些工具; 另一条是怎样和低级繁琐且不顺手的 FORTRAN 语言做斗争, 克服语言的障碍, 写出功能和可读性俱佳的结构化程序. 第一条着重强调的是一个系统的功能分解(对UNIX哲学清楚的读者看一下目录就一目了然), 第二条实际上是叙述了一个一脉相承到”代码大全”的哲学: 如何构建”你的”编程语言, 而不是简单的使用”别人的”编程语言. 这一条, 道出了整个编程的真谛: 编程就是构建一个一个”自己的”小积木, 然后用自己的小积木搭建大系统.

为了说明小积木的道理, 我们从编程语言说起. 我以前的文章也提到过, C 并没有一个可以传递一行消息出来的 Assert 机制. 因此有经验的程序员会自己构造一个 Assert. 同样的道理, Java 虽然很高级, 却没有一个很好的单元测试框架, 所以全世界 java 程序员都在用 JUnit. 这些实践, 表明了一个现成编程语言总有一些特性不完美之处, 工具和使用者之间还有着不小的距离, 因此显得”不顺手”. 如果这个例子不够说明问题的话, 不妨问自己: 为什么人不能像写伪代码一样写程序呢? 因为我们使用了编程语言, 而编程语言有很多肮脏的细节要我们去处理, 比如下标从0开始, 浮点数不好作为数组下标等等. 语言的细节需要处理这个问题, 从 Fortran 到 Python, 只有程度的改变, 并没有本质的改变. 况且, 通用编程语言之所以通用并且简单, 就是因为支持的功能比较基本, 可扩展性强. 因此, 基本功能都有, 高级功能缺少成了通用编程语言的最大特点. 不管编程语言多么”高级”, 总是没有自己的思维高级. 因此, 编程的第一步就是把语言改造成自己的语言. 即使强大到直接能 [url=http://xkcd.com/353/]import antigravity 的 Python, 也有需要改造的地方(最好的例子就是 Python 3000 的推出).

小积木有了, 就要构建大系统了. 在这一点上, Software Tools 可以说是非常好的一本源代码导读. 自从 [url=http://en.wikipedia.org/wiki/Lions]Lion 分析 Unix 源代码以来, 源码剖析成了程序员修炼的一个捷径. 可是现在程序的源代码树都很繁杂, 能真的拿出来分析的很少很少了. Bell 实验室的两位作者从实作 UNIX 系统下的工具出发, 挑选出经过实践检验的优秀代码来讲解. 这样来自一线的题材是极其宝贵的, 就算在最新的 Beautiful Code 中, 大多代码也只是教科书代码而已. 至于代码大全, 完全就是玩具代码. 而 Software Tools 有几千行代码的大程序, 也有几行代码的小程序; 有算法程序, 也有文件IO程序, 基本覆盖日常所有用例, 对于内功修炼大有裨益.

除了道出”改造你的语言”的真谛之外, 这本书其他论点也可谓字字珠玑. 比如讲goto带给程序员的自由恰好是你不想要的自由, 因为这个自由会带来很多错误. (很多语言都有这种不想要的自由, 比如 C++, 到处都是). 比如说讲结构化编程不会自动带来清晰的程序, 因为机械的规则永远不能代替清晰的思考. 这个道理在面向对象/设计模式领域也一样. 比如本书还论证了为啥要详细设计, 因为设计和编码环节对于程序员讲是愉快的事情, 值得更多投入. 而 debug 和 测试环节是比较痛苦的事情, 所以要少投入. 还比如人比机器时间贵, 所以程序员要越懒, 越快完成编程越好. 除非程序太慢, 否则从总成本看, 机器多用点时间没事, 人用的时间要越少越好, 等等等等. 类似于这样的深刻揭示编程的哲学理念的句子俯拾皆是, 比起相同内容但是篇幅冗长的代码大全, 这本书适合随身携带, 随时阅读, 随时提高.

老规矩, 结尾顺手说个八卦吧. 话说为了把 Ratfor 这个假想的语言翻译成当时最流行的两种语言, FORTRAN 和 PL/I, bwk 爷爷写了一个宏替换的工具, 能够把 Ratfor 替换成肮脏的 FORTRAN, 而他们写干净的 Ratfor. Dennis Ritchie 爷爷看到鸟, 很赞, 于是推广了一下这个宏替换工具, 起个诡异的名字叫做 m3 (macro for ap3). 然后 bwk 爷爷又看到了 dr 爷爷的工作, 回过来又和 dr 爷爷合作, 写出了金光闪闪的 m4. 如果你常常编译开源软件, 肯定会注意到一个叫做 configure 的生成 makefile 的程序. 这个 configure 的读入, 一般情况下是可配置的, 叫做 config.ac, 就是 m4 语言写的. 虽然因为版权问题, 现在 GNU m4 和两位爷爷没啥关系了, 但是基本的语法和用法都是一样的. 各位知道 K&R 的读者千万不要错过这个好用的工具(也是编程语言).

这个工具其实我也只懂皮毛, 也不常用, 只是用来自动编号一些行, 做一些稍微复杂一点的不能用正则的文本替换. 不过我似乎在某个地方听一个高手说, Linux 命令行下文本处理三剑客乃是 sed/awk/m4, sed 和 awk 的强大早就见识了, 相必m4与他们各有千秋. 故而略介绍一下.

另外, 本书也是 troff 排版的. 按照我的 [url=http://blog.youxu.info/2008/10/13/trivia-about-computer-typesetting/]troff 排版无烂书定理, 这本书也属一流好书.
6#
 楼主| 发表于 2010-5-24 15:23:41 | 只看该作者

编程珠玑番外篇-6.高效能编程的七个好习惯

原文: [url=http://blog.youxu.info/2008/10/29/seven-habits-of-highly-effective-programmers/]http://blog.youxu.info/2008/10/29/seven-habits-of-highly-effective-programmers/

这七条都是我这个不怎么高效能编程的人悟到的. 不权威, 不一定全对.

1. 使用工具帮你找 Bug, 而不是人工找.
工具包括用单元测试, assert语句, 代码测试容器. 人工指用 print 和 debugger 一行一行跟踪. 我们知道, 编程中绝大部分时间是耗费在除 bug 上. 不同的人有不同的 debug 的方法. 我个人比较喜欢”极限编程(XP)” 学派的主义, 也就是说, 代码未动, 测试先行.
单元测试中的红棒绿棒(熟悉 JUnit 的读者知道我在说什么)一出现, 哪里出了问题就一目了然. 单元测试的另外一个好处在于增加写程序的自信. 以前没用单元测试之前, 每天晚上改代码改到很晚的时候脑子常常不灵活, 把代码改错, 然后第二天来还要重头弄. 有了单元测试之后每天晚上保证测试全部过掉, 这样心理踏实, 睡觉也香, 早晨也不忙, 吃饭也棒.
一般的语言都有 assert, 但是很少有人用. 其实 assert 是一个非常好的DEBUG 工具, C 的 assert 能够把哪一个文件哪一行出了错都告诉你. 不过我一般会自己写一个这样的 assert 宏:
#define ASSERT(value, msg) if (!(value)) {fprinft(stderr, "At file %s, line %d: \n message: %s\n", __FILE__, __LINE__, msg); exit(-1);}
这样的 ASSERT 可以带一个信息出来, 比起原来只告诉你哪个文件哪一行更加有价值.
第三个是用容器帮你找 Bug. 这一点以 C/C++ 程序最为突出, 因为编译之后直接就是可执行代码, 运行时的信息不像 Java 和 Python 这样有 VM 的语言容易得到. 这时候, 我推荐 [url=http://valgrind.org/]valgrind. 这个工具能够把 C/C++ 程序放到一个容器中执行, 记下每一个内存访问. 被这样的容器 debug 一下, 基本上指针指飞了 (Segmentation Fault) 的情况几乎就没有了. 想像一下是用 GDB 追踪非法指针和内存泄露方便, 还是用容器告诉你哪一个指针非法, 哪一个内存没释放方便

2. 选用自动化工具构建
用 gcc 或者简单的 IDE 来编译和运行程序在编程初期是很快速的, 可是越到后来, 会越臃肿. 在编译的时候, 不同的参数, 不同的目标, 在 IDE/gcc 里面每次都要设定. 而且一般的 IDE 也不能做到自动解决依赖等高级方法. 因此, 最好的方法是用 [url=http://ant.apache.org/]Ant 或者 Makefile 管理项目. 这方面教程很多, 而且我估计编程的个个都知道. 不管项目大小, 注意频繁使用就是了.
自动化测试也有很多工具, 特别是 GUI 和命令行测试的自动化, 工具链都很完整. 大公司里的程序员走这方面的流程都比较规范(我在西门子实习过), 但是小一点的公司中, 或者个人搞小项目的时候, 就不一定想得起来了(大部分我见到的程序员就手工来测试). 手工测试看上去快, 但是要是积累的次数多了就比较浪费时间了. 其实自动化测试工具的学习成本很低的, 事半功倍.

3. 买本小书做参考, 而不是用 Google.
这是大实话. 我大三开始学 Python 的时候, 语言特性并不熟悉, 手头也没有书, 因此常常连取个随机数都要上 Google 查一下库. 我发现, 不管网络多快, 自己搜索技术多牛, 还是没有手头一本书方便. 后来打印了一个7页的标准库的 cheatsheet, 编程立即行云流水. 我在实习的时候也观察到, 大部分时候程序员不可能记住一个框架所有的API, 所以他们要不等 IDE 几秒钟做代码补全, 要不一边翻文档一边做. 或许MSDN 这些本地文档系统比查书快吧, 但是用 Google 和网络搜索绝对比书慢. 现在因为工作原因, 常常要学一些新的语言, 我做的第一件事情, 就是把他的库接口的网页全部打印了下来.

4. 用脚本语言开发原型
人月神话的作者 Brooks 说: 准备把第一版扔掉, 因为第一版必然要被扔掉. 这是大实话和真理. 既然第一版要被扔掉, 咱们就让第一版扔掉得越早越好. 说白了就是, 原型要快速的被开发.
所谓的快速原型开发, 大致有两个捷径, 第一是只做核心的功能, 输入输出都是构造好的简单的例子. 第二是只做最简单的情况, 对于性能和健壮性什么的都不太考虑. 这两点, 恰好是脚本语言最擅长的. 脚本语言擅长于用精简的几行构造出复杂的功能, 并且语法很松散, 潜在假设程序是正确的.
即使在代码编写阶段, 一些功能的实现, 也是要先写个简单的, 再慢慢打磨成复杂的. 脚本语言此时依然有用. 比如我在用 Java 的时候, 常常不确定一个函数返回的对象究竟某个属性是什么样的值. 这时候我就会用 Java 的 bsh 脚本写一行打印, 而不会写一个复杂的 out.println 再编译再运行再把那行删除掉. 当然, 这几年很流行动态语言, 原型和产品之间的差距已经变得很小了.

5. 必要的时候, 程序要使用清晰的, 自我解释的文本文件作为日志输出.
不知道各位调试程序的时候是不是和我一样, 看到不确定的和要跟踪的变量就直接插入一行 print. 我以前一直这样做, 但是频繁的插入这样的打印会使得屏幕的输出很乱, 不知道哪行是什么意思. 一个更加好的办法是写一个日志函数, 可以分也可以不分优先级, 总之保证 Debug 的时候的输出以一种统一的, 可管理的方式出现. 这样, 在最后发布稳定版本的时候, 只需要简单的几行命令就可以从代码中剔除所有的日志打印行.
如果必然要输出日志, 最好要分配一个单独的命令行参数, 用来控制程序究竟输出不输出日志, 输出哪些日志. 一开始看上去这个是费时费力, 越到后来日志越多的时候, 就体会到方便之处: 有时候你只想要某一类日志, 可是其他的记录偏偏来捣乱. 多加一个参数可以使得程序更加灵活, 根本不需要去修改代码或者条件编译就能得到不同级别的程序日志.
日志和程序的输出结果一定要清晰且能自我解释, 否则不如没有日志. 我切身经历是这样的: 几个月前, 我一个程序跑了大约一天, 最后输出了很大的日志和结果. 但是很不幸的是, 结果里只有数字, 没有任何说明. 我自己都忘了每一行是什么意思. 而且更加麻烦的是程序的输出藏在重重判断和循环之内, 使得根本没有办法分析这一行输出对应的输入是什么. 于是, 最终只能再次浪费一天的时间让程序再跑一次.  经过这次教训, 我的程序日志和结果中插入了不少让人可读的内容. 这样, 即使程序丢失了, 结果还是能够被人解读的.
更多的关于数据和程序结果要能自我解释的精彩论述, 可参见 More Programming Pearls 第四章.

6. 使用命令行小工具操控分析你的结果和代码, 而不是用自己的眼睛和手.
我发现, 人有一个固有的习惯, 就是喜欢自己去”人工”, 而不喜欢用工具. 因为人工让人感觉工作更加刻苦, 更加快, 更加有控制感. 比如说吧, 上面我说的测试, 我就不只一次见到为了测一个交互式的命令行, 一个程序员宁愿老是每次打相同的三个命令, 而不愿意用一个简单的 expect. 再比如说, 面对长长的日志文件, 我见到很多人都是用文本编辑器直接打开, 用鼠标滚轮一行一行的往下翻, 而不是使用 grep. 包括看网页, 很多人从来不用查找功能, 而是一行一行的往下瞄. 包括打游戏也是, 好的UI脚本(不是外挂)一大把, 可是玩 WoW 的人很少用, 都喜欢自己重复点鼠标.
别看上面说的这些好像程序员没有, 其实我们常常陷入这个误区. 举个简单的例子, 一个 python 程序里面有十几个 print 函数, 我们想把这些打印全部灭掉, 一般人会打开文件慢慢瞄, 稍微高级一点的用查找, 找到了, 用快捷键删掉整行. 其实最好的方法根本都不要编辑器, 应该用 grep -v. 或者 sed, 但是这样的方法极少会有人用的. 我也是强迫自己无穷多次之后, 才渐渐的用这套快速的方法.

7. 程序能跑就是万岁. 除非万不得已, 尽量不要在性能上优化你的代码
Knuth 名言: Premature optimization is the root of all evil. (提前优化是万恶之源). 一般我们写代码的时候, 不知不觉的就会觉得, 哎呀, 这样写效率不高, 我要构造一个数据结构啥啥. 随机访问一定要哈希表, 排序一定上快排, 查找一定要二分, 强连通分量一定要用 Tarjan 算法, 动规一定比穷举好等等, 这些竞赛的时候极限情况下正确的论断其实在实际环境中并不重要, 因为做编程的一开始关键是能跑, 而不是跑得快. 往往这么以优化, 程序很难 debug, 倒是还要去翻算法导论和TAoCP 看人家的二分怎么写的等等.
在程序能跑的情况下, 优化也要特别小心. 我曾经有一个程序, 大约有 90% 的运算是查表, 只有 1% 的是乘法, 另外是一些判断和把插到的结果插入到一个集合中. 我的查表是用的最土的 list.index. 按照正常的想法, 应该把这个优化成哈希表. 而实际上我用 profile 工具一看, 才知道, 原来是插入到一个集合的操作费时间, 因为每次都需要 extend, 涉及到很多内存分配的操作. 我做过非常多的 profile 测试, 没有一次不出乎我预料的. 程序运行时间总是在自己不认为浪费的地方被浪费掉. 因此, 就算万不得已优化, 也务必要先做一下 profiling. 我喜欢 python 的地方就在于, 他的 profiling 只需要一行语句就完成了, 而且结果具体干净. 其他的语言, 至今没见到这么简单的 profiling 工具.

另外: 用两个或者大于两个显示器. 不要用或者少用鼠标.
7#
 楼主| 发表于 2010-5-24 15:25:57 | 只看该作者

编程珠玑番外篇-7.比代码大全好的两本书B

原文: [url=http://blog.youxu.info/2008/11/23/the-elements-of-programming-styl/]http://blog.youxu.info/2008/11/23/the-elements-of-programming-styl/

各位读者老大中有不少都是大学生, 相信不少都参加过形形色色的英语写作培训班. 如果当年您参加培训班的时候, 老师没有介绍一本叫做 <[url=http://en.wikipedia.org/wiki/The_Elements_of_Style]The Elements of Style> (TEoS) 的书, 建议您现在立即冲过去找他们退钱. 为啥呢, 因为这本书是讲解英语写作绕不开的经典圣经(即使这本书已经被说烂了, 批评也不少, 但还是经典). 假如培训机构或者老师上课没推荐到这本书, 这个培训机构要不是太牛逼了, 要不是水货. 而大家都知道, 水货和牛逼的比例总是 1:epsilon.  

作为[url=http://www.amazon.com/Elements-Style-Fourth-William-Strunk/dp/020530902X]Amazon 上 297 个5星的书, 书评我就不狗尾续貂了. Knuth 爷爷也是很喜欢这本书滴, 因此在 Stanford 开课的时候让学生人手一本 (我们系今年新生也强制人手一本). 这本书不光勾勒了英语的基本写作要素, 也刻画了一个时代: 从此, 任何需要”艺术和技艺”的领域, 都会时不时跳出一些牛人, 模仿这本书的题材和哲学, 用简洁的文笔勾勒出这个领域的基本要素. 以我熟悉的计算机领域为例, 就有 “The Elements of Programming Style”, “The Element of Programming Style with Perl”. “C Elements of Style”, “The Elements of Java Style”, “The Elements of UML Style” 等等书, 都是希望继承 TEoS 的衣钵, 勾勒出编程的一些风格要素. 今天我要说的比<代码大全>好的书的第二本, 就是叫做 <The Elements of Programming Style>的. 我以前在[url=http://blog.youxu.info/2008/04/09/classics-in-cs/]计算机科学必读经典中, 也提到了这本书.

这本书作者和上一本 Software Tools 一样, 属于一个家族哲学下的两本不同角度的书. 关于它的书评也很多, 我就不一一废话了. 只说几个体会较深的.

第一是写程序和写作一样, 要写的清楚. 这本书翻开第一条就是 Write clearly – don’t be too clever. 看上去说的和没说一样, 其实实践起来乃是金科玉律. 我曾自己写过三层嵌套的 “? :” 表达式, 写的时候自己被自己的聪明都感动了, 回来改的时候自己被自己当时的聪明给打击了: 死活看不懂当时啥意思, 只好写一个 printf 在后面测输出. 假如当时多花几分钟写的清楚一点明白一点, 就犯不着回头修改的时候花半小时破译了. 现实中的情况没这么极端, 但是也比比皆是. 相信任何正常的程序员, 每天都要为了理解以前写的不大清楚了程序浪费不少时间 (反正我是记不住一年前写的代码的每个小细节). 因此, 写的时候写的清楚比什么都重要.

在写得清楚上, Knuth 爷爷是榜样. 他提出的 [url=http://www.literateprogramming.com/]Literate Programming 的思想虽然太学术, 使得实践的人不多, 但是的确使得程序更加好读. Knuth 爷爷把他的用C语言作为基本语言的 Literate Programming 系统叫做 CWEB. 大名鼎鼎的 TeX 就是 CWEB 写成. 如果对 Knuth 爷爷比较粉的粉丝们恰好要做图算法,  Stanford Graphbase 是一本非常好的书, 里面贴得全是程序, 但是因为 Knuth 爷爷用 CWEB 写成, 文档和程序浑然一体, 读起来丝毫不觉得思维在程序和自然语言间做切换. Java 下有名的 XDoclet 和 Javadoc, 事实上也是 Literate Programming 的一种体现. [url=http://tex.loria.fr/historique/interviews/knuth-clb1993.html]据 Knuth 爷爷讲他写 CWEB 程序能笑出来, 这种境界不是一般人能有的. 而且 Knuth 爷爷在提出 Literate Programming 的时候, 就野心勃勃的说: 写文章也是写, 写程序也是写, 我们 Literature Programming 的口号就是: 没有蛀牙 程序员也能拿普利策. (“I’m hoping someday that the Pulitzer Prize committee will agree.” Prizes would be handed out for “best-written program”.)

又八卦走题了. 言归正传, 我的第二个深刻的体会是”让计算机干脏活”. 什么叫脏活呢? 让你不爽的活叫脏活. 比如 Debug, 比如无穷多的复制粘帖, 比如替换一个大小写, 数数几个单词, 做做单元测试等等. 用眼睛瞄肯定会死人. 我以前在 “[url=http://blog.youxu.info/2008/10/29/seven-habits-of-highly-effective-programmers/]高效能编程的七个好习惯”  这篇文章中也说了, 就不多废话了.

当然, 现实的问题是, 理论是理论, 实践是实践. 事实上, 我们要不然就是不用或者想不起来用工具(理由是不习惯), 要不然就是成为工具的奴隶. 李笑来老师也观察到了第一点, 比如[url=http://www.xiaolai.net/index.php/archives/1491.html]这篇. 为什么明明别人告诉我有高效率工具和习惯存在的情况下, 我们还不去用不去改, 或者如何不成为工具的奴隶这两个话题都太大了, 我也写不好, 就不废话了. 然而, 不管最后实践用还是不用, 读一些被别人实践检验过的经验之谈还是很有用的. 这也是我推荐这本书的原因. 不知道大家有没有发现, 潜意识中如果有个正确的小声音不时在原则上提醒自己, 实践的时候潜移默化的就会越做越好.

最后依然附送两个八卦. 第一个是关于 TEoS 这本书的. 这本书列了很多的原则和规则, 都是具体的对某个词某个句型的建议, 因此英语写作的时候可以直接应用这些规则. 不过对着书查规则显然属于脏活的范围, 所以呢, 我们的”让计算机做脏活”的哲学就发挥作用了: 在 Linux 下有一个程序叫[url=http://www.gnu.org/software/diction/diction.html]diction, 用他可以检查英语写作的文章符不符合 TEoS 的标准, 我[url=http://blog.youxu.info/2007/07/11/some-useful-tools-for-you-to-write-english-articles-on-linux/]以前也专门介绍过. diction 会挑出那些不符合 TEoS 的句子, 告诉你让你修改. Knuth 爷爷也说, 虽然这个程序很笨, 但是至少可以强迫你重新审视你的文章, 挑出弱智的错误. 其实 GNU/Linux 下帮助英文写作的工具很多, 虽然不完美, 也称得上完整了. [url=http://blog.youxu.info/2007/07/11/some-useful-tools-for-you-to-write-english-articles-on-linux/]我以前的文章可供大家参考 . 和 diction 一起的另一个工具叫做 style, 可以做像长句分析, 被动语态分析, 平均单词和词汇量估计等统计, 以及语言学水平上的英语水平估计(等价于美国几年级学生水平的估计). 这些估计都是语言学家研究数年的标准指标. 大家都知道, GRE 作文是计算机批阅的, 虽然我们不知道算法, 但是可以想象, ETS 那么笨, 肯定是请语言学家帮忙设计的程序, 所以必然或多或少的用到很多标准的语言学指标. 所以呢, 你不用计算机程序分析分析自己的文章, 光听培训机构的一些老师忽悠, 怎么知道自己文章水平呐? 相比较一些培训机构的老师, 指不定 style 这个程序更像 ETS 的评价标准.

第二个八卦是关于写清晰的程序的. 或许大家都听说过史上最牛逼的注释的故事. 虽然各人有个人认为的最牛注释, 我个人喜欢的叫做 /* You are not supposed to understand this. */ (我不指望你懂这是啥意思). 这句话其实本来不该这么出名的, 恰好是因为出现在开源的第六版UNIX中, 恰好写的人是 Dennis M. Ritchie, 恰好澳大利亚出了一个叫 Lion 的人把 UNIX 源代码扒出来搞了个源码解析, 又恰好当年这本源码解析几乎每个黑客都人手一本. 所以, 这个极其挑战其他黑客智力的注释就变得流行起来鸟. [url=http://cm.bell-labs.com/who/dmr/odd.html]DMR 同学对此有技术上的详细解释,  不再废话. 就是友情含泪劝告读者: 您要是在你的程序里面搞这么一句然后又被你同事和老板看到鸟, 你就完蛋鸟. 世上只有一个牛逼的 DMR 敢这么写.  

PS: 想要看看The Elements of Style 书的内容的老大们, 可以猛点[url=http://www.crockford.com/wrrrld/style.html]这个链接:

想要看 The Elements of Programming Style 说了哪些的老大们, 可以猛点[url=http://www.cnblogs.com/hitszxin/archive/2008/04/03/1136726.html]这个链接

-EOF-
8#
 楼主| 发表于 2010-5-24 15:28:04 | 只看该作者

编程珠玑番外篇-8.Smalltalk 中的珠玑

原文: [url=http://blog.youxu.info/2008/11/30/pearl-in-smalltal/]http://blog.youxu.info/2008/11/30/pearl-in-smalltal/

如果我们能够重回1980年, 回望整个计算机编程语言领域, 特别是工业界编程, 打死也不会想到日后 Java 这种无名小卒, 以及 C++ 这个又面向对象又支持过程的双面间谍能够红得发紫. 当年最流行的语言, 当属 FORTRAN, C 和 Smalltalk. 前两个我们按住不表, 单说这个 Smalltalk. 我们现在的教科书基本都不介绍 Smalltalk, 或者就用一句: Smalltalk 是第一个纯面向对象的语言 概括过去. 其实 Smalltalk 中有很多的好的思想, 一直在今天都发挥着魔力.

施乐当年的图形界面(来源: harding.edu)


为提起大家兴趣, 我先说血统和设计等八卦. Smalltalk 的血统是算得上高贵的, 来自当年超级牛逼的 施乐 PARC 实验室. 施乐的 PARC 干过很多事情, 比较著名的一个故事是说乔布斯同学去参观, 看见那边科学家已经做出了 GUI (图形界面程序), 于是偷偷的回家搞 Macintosh, 搞好之后在1984年发布, 卖得大大的好, 赚得盆满钵盈. 西雅图当时有个大学没毕业做软件的小伙子, 看见乔老师赚了大钱, 想想觉得自己的人生挺没意思的, 只是和 IBM 做订购 DOS 的生意, 于是起了自立为王的念头; 加上看到乔老师的苹果机一个窗口一个窗口的很好玩, 于是一激动就自己搞了一个 Windows. (这个作软件的小伙子就是比尔盖茨啦). 这小伙子很牛, 把乔老师的苹果机逼到了角落里. 乔老师是最不能咽下恶气的人, 于是连在 Stanford 演讲了时候还不忘提一下微软抄苹果. 法律上就更不要说了, 两家公司之间旷日持久的 GUI 专利权官司从1988年打到1994年. 两家公司都一步不让. 最后施乐火了, 跳出来大喊一声: 靠, GUI 乃是我发明的. 于是把苹果给告了. 所谓螳螂捕蝉, 黄雀在后, 苹果被施乐这么一搞, 自己抄别人的老底就被挖出来了, 告微软就显得特别勉强, 所以官司最后也没赢, 以苹果无理取闹失败为结果.

施乐不光用 GUI 引领了我们现在计算机图形界面, 还发明了以太网, 鼠标, 所见即所得的编辑器等. 要不是这几样东西, 现在的计算机说不定是另一个样子呢. 言归正传, 前有施乐 PARC 出品了这么多伟大产品, 后加上 Alan Kay 这种牛人主导设计, Smalltalk 的血统之好, 和出自 AT&T Bell 实验室的 C 是有一拼的. C 还是两个人无聊敲打出来的, Smalltalk 是正儿八经作为一项研究弄出来的产品.  

事实上 Smalltalk 的确也是划时代的产品. 我就说我知道的两个部分.

第一是现代程序员耳熟能详的 [url=http://en.wikipedia.org/wiki/Model-view-controller]MVC 结构以及整个 Design Pattern 的思想. MVC 出现在 Smalltalk 中并不是偶然的. 当年施乐开发 Smalltalk 主要是用来做图形界面编程的, 而图形界面的编程首先就是从施乐发明图形界面开始的. 试想一个程序员成天写命令行程序, 肯定是不会太在意 MVC 的分离. UNIX 世界中并没有MVC的对应物, 因为压根不需要. 而图形界面程序的复杂度比其他程序要高太多了, 因此自然的就产生了 MVC 这样解开功能模块耦合的自然的设计. MVC 的重要程度和流行程度可以从两个小事情看出来. 第一是著名的 [url=http://en.wikipedia.org/wiki/Design_Patterns]GoF 书, 翻开第一章第二节就开始讲 MVC, 用 MVC 作为整本书的纲领章节, 可见其重要程度. 第二是众多的 Java 框架, 比如Struts, JSF, 里面的对象就很直白的叫做 XXModel 或者 XXViewer. 这些传统都是从 Smalltalk 开始的, MVC 的影响一直到今天还到处都是. Smalltalk 不光催生了 MVC, 也催生了 Design Pattern. 细心阅读 GoF 的 DP 书我们就会发现, 里面所有的 Pattern 大多是在设计一个所见即所得的编辑器的背景下提出来的. 而上面我们已经说了, 施乐是第一家搞这个玩意的. 如果我们追溯 Smalltalk 早期很多的论文, 很明显可以看出, 虽然没有用 Design Pattern 这个词, 开发的时候要遵循一定的”对象结构”的思想是随处可见的.

第二是我认为非常重要的: 运行时类型信息支持, 或者叫反射. 简单的说, 就是一个对象在运行的时候能够知道自己的类型(类名称), 以及这个类有哪几个方法, 哪几个字段等等.

关于反射的基本概念在脚本语言里面是屡见不鲜的了. 大家都知道, LISP 里面的 eval 后面可以加任何的字符串, 构造出一个运行时对象. 脚本语言实现反射也很简单: 本来就是解释执行的语言, 多一个 eval 等价于多调用一次解释器而已. 而编译型语言就麻烦了, 因为解释器已经在编译期用过了, 运行的时候解释器是不存在的. 这样, 就造成了编译型语言没有运行时信息这个本质困难. Smalltalk 用了一个巧妙的方法解决了这个问题, 也就是 Java 和 Python 等现代语言用的方法: 虚拟机. 能编译的代码被先编译, 需要解释的代码在运行时可以被虚拟机自带的解析器再解析. 除了加入一个小的解释器到虚拟机外, Smalltalk 更进一步, 把对象的元信息也抽象成一个对象, 这样运行时需要的一个对象的所有元信息都能在面向对象的标准框架下表达. 我们用类 Java 的语言来举例: 一个叫 a 的 Foo 对象, 包含一个 a.hello() 的方法, 这个方法既可以通过 a.hello() 来调用, 也可以通过 a.class 先得到 a 的类, 再通过 a.Class.findMethod(“hello”) 找到这个方法. 最后再通过 .invoke() 调用这个方法. 这样的流程在没有虚拟机的 C++ 里面是没法完成的.

在1980年, 这个反射机制的划时代意义是怎么说都不为过的. 我以我熟悉的 [url=http://www.junit.org/]JUnit 的进化史为例说明这个议题.

现在做单元测试的框架, 一般都被称为 xUnit 家族. xUnit 家族最早的成员, 不是 JUnit, 而是 SUnit (Smalltalk Unit). SUnit 的历史比 Junit 悠久得多, 大约在1994年的时候, [url=http://en.wikipedia.org/wiki/Kent_Beck]Kent Beck, 也就是 Junit 的作者之一, [url=http://www.xprogramming.com/testfram.htm]写了 SUnit. 而后才有了 JUnit (1998). 所以, 在 [url=http://sunit.sourceforge.net/]SUnit 的网站上, 极其显摆的写着”一切单元测试框架之母” (The mother of all unit testing frameworks). 事实上这是大实话 — 所有单元测试框架里面的名词术语, 都从 Sunit 来的, 如 TestCase, Fixture 等等.

既然 SUnit 和 Junit 是同一个作者, 而早在1996年, Java 就已经成为工业界炙手可热的语言, 为什么要等到两年之后, JUnit 才横空出世呢. 这里面的原因说简单也简单: 自动单元测试需要反射支持.  1998 年前的 Java 没有反射, 直到1998年 Java 1.2 发布, 反射才完整的被支持. 所以, 只有1998年之后, Java 才有办法做自动单元测试.
我们回顾一下 Junit 的工作流程: 继承一个 TestCase, 加入很多以 test 开头的方法, 把自己的类加入 TestSuite 或者直接用 TestRunner, 让测试跑起来. Junit 能够自动覆盖所有 test 开头的方法, 输出红棒绿棒. 这地方的关键是自动覆盖. 假如每个测试都是靠程序员自己写 printf 比较, 那不叫自动. 假如每个 TestCase 里面的每个 test 开头的方法都要程序员自己写代码去调用, 那也不叫自动. 所谓的自动, 就是在机器和人之间形成一定的规约, 然后机器就去做繁琐的工作, 最小化人的工作(RoR就是很好的例子).

注意到我们的需求是 “让 Junit 自动调用以 test 开头的方法”, 而不需要自己很笨的一个一个自己去调用这些方法. 这意味着 Java 语言必须支持一个机制, 让 JUnit 知道一个测试类的所有方法名称, 然后还能挑出 test 开头的方法, 一一去调用. 这不就是反射么! 事实也证明了这一点: 目前互联网上找到的最早的 Junit 的源代码, 1.0 版的核心就只用了一个 Java 的标准库: reflect. 相反, 不支持反射的语言, 就得告诉单元测试的框架我要运行哪些. 比如说 C++ 的单元测试框架 CppUnit, 就很不方便–必须告诉框架我要测哪几个函数, 就算他们以 test 开头也不行. 还有一个好玩的例子是 J2ME 的测试框架. J2ME 是 Java 小型版, 不支持 reflect, 因此, JUnit 平移不上去. 如果细看所有的这些移植 JUnit 的尝试, 很容易发现, 移植出去的版本作用到有反射机制的语言上, 使用起来就很方便, 就比较成功, 比如NUnit; 没反射机制的就比较麻烦, 用的人也相对少, 比如 CppUnit 和 J2MEUnit. 反正任何对于 JUnit 的移植, 都绕不开”反射” 这个机制. 有反射者昌, 无反射者弱. NUnit 这个移植版本, 还曾经被 Kent Beck 夸设计好, 其原因, 与 C# 语言比 Java 更加良好的 attribute 和 反射机制, 是息息相关的.
此外, 现代框架中流行的 依赖注射 (Dependency injection), 反转控制 (Inversion of control), 都是基于反射的. 这也就是为啥用传统的不支持反射的语言很多年的人很少听过这些名词的原因.

有兴趣的读者可以继续阅读 wikipedia 关于[url=http://en.wikipedia.org/wiki/Reflection_(computer_science)]反射 和 [url=http://en.wikipedia.org/wiki/Metaprogramming]元编程 这两篇文章, 相信会得到更加多的启示.

Smalltalk IDE (arstechnica.com )


除了以上两点, IDE 和库的思想. 我们今天用的标准名词, 如”方法”, “字段”, 都是来自于 Smalltalk 的. 这些也都是划时代的工作, 因为我不熟悉, 也不敢不懂装懂的展开介绍了.  

有时候回看历史, 特别是回看编程语言的设计和进化的历史, 会发现很多散在的晶亮的珠玑.

(完)
9#
 楼主| 发表于 2010-5-24 15:29:57 | 只看该作者

编程珠玑番外篇-9.快工具, 新思想

原文: [url=http://blog.youxu.info/2008/12/04/wikiwik/]http://blog.youxu.info/2008/12/04/wikiwik/

和世界上大多数国际机场一样, 美国夏威夷国际机场非常大. 为了方便旅客在航站之间转运, 航站之间用巴士提供交通服务. 在夏威夷, 他们用本地人的语言把这种巴士命名为 wiki wiki, 意思是”很快”. 因为在本地人的语言里面, wiki 是”快”的意思.

1995 年的时候, “极限编程”方法论大牛, [url=http://en.wikipedia.org/wiki/Ward_Cunningham]Ward Cunningham, 觉得应该建立一个公共的网站, 让人能够输入一个 Pattern 的名字, 就能查阅到一个 Design Pattern 的用法, 而且这个网站还能被人编辑, 实现知识共享. 从此, 世界上第一个 wiki 网站就建立起来了, 他把它的东西叫做 [url=http://www.c2.com/cgi/wiki]WikiWikiWeb, 意思就是”快速查阅的网站”. 这时候 wiki 还只是在程序员之间流行, 直到 2001 年, 一个叫 Jimmy Wales 的, 创建了 Wikipedia, 从此, 才算是普及了. Wiki 和 Wikipedia 彻底改变了我们的生活. 试想, 人类协作创造了一本共享智慧的, 随时可访问(中国大陆和朝鲜除外)的百科全书, 是多么值得荣耀的伟大成就!

且慢, 以前人类难道没有百科全书么? 有, 几乎每个像样的图书馆都有大英百科全书, 为什么这些百科全书没有如此大的改变我们的信息获取方式呢? 沿着同样的逻辑链条, 我们可以问更多的问题: 在没有 Google 之前, 似乎搜索引擎也有, 但这个东西我们很少用, 也很少听说, 为什么就是 Google 一出现, 就彻底改变了我们的检索方式呢?

问题的答案很多, 我说我的答案: 很多事情, 只有在人能很快的完成的时候, 才有了做的可能. 这句话可能比较拗口, 反过来说可能更加好懂: 如果用某种方法做一件事情太耗时间了, 那么人就不可能用这个方法做事情. 只有一个方法能够让人足够快的做好事情的时候, 这个方法才会变得实用, 同时这个事情才有做的可能性.

为避免过于抽象, 我们仍然用例子说明. 美国宪法规定要10年做一次人口普查, 但是直到 1890 年, 美国才进行了历史上第一次完整的全国人口普查. 传统上, 人口普查的数据提取上来, 要花多于10年的时间才能处理完, 因此, 人口普查从来就做不完. 直到1890年左右, IBM 公司发明打孔卡片, 卖给了美国人口统计局, 美国人口统计局采用了打孔卡片作为报表, 从此才能在2年内做完一次人口普查统计. 打孔卡片比人填表统计快多少呢, 也就快5倍而已. 但是就这个5倍, 把原本不可能做到的全国人口普查变成了可能.

天气预报也是很好的例子. 天气预报的原理是解一个数值偏微分方程, 这个道理科学家在1922年就知道了. 但在计算机没出现前, 是没有天气预报员这个职业的. 直到1955年, 在电子计算机的帮助下, 天气预报才变成了现实. 那么, 1955 年的电子比1922年的机械式计算机快了多少倍呢? 也就1000倍. 另外有一个未经证实传说说, 以前预报24小时内的天气预报需要计算机计算25小时, 直到更快的计算机出现, 才使得2小时之内可以算出24小时的天气预报, 使得天气预报实用化. 这个, 也就是10倍的更新.

快工具和慢工具的差别, 带来了一件事情可做与不可做的差别. 其实不光是表面上速度的改变, 对应内里是整个方法体系的本质改变才是关键. 比如, 在 UNIX 下数一个文档有几个a开头的词是很简单的事情, 只需要知道正则表达式和管道就行了. 在没有正则表达式和管道的环境里, 这个事情就比较难 (或者有更加好的方法我不知道?). 当然, 这事也可以做, 只是慢了10倍而已. 同理, 从纽约到华盛顿步行也能到, 就是慢了一点而已. 而汽车让从纽约到华盛顿变成了一件很平常的事情, 其实小汽车也就比步行快了不到20倍而已. 到图书馆查百科全书也是一种获取资料方式, 在网上 Google 也是一种方式, 后者(一分钟)比起前者(一小时), 也就快了10倍到100倍而已. 可不同的仅仅是速度么? 正则表达式提供了新的描述角度; 汽车是一种新的不耗费体力的快速交通工具; Google 是一种新的获取信息的手段, 这些速度的表面差异, 对应的内里, 是本质差异. 虽然改变的是速度, 却不仅仅是速度. 甚至, 我们可以大胆的断定, 如果没有本质的内里的变化, 速度也不可能有10倍的提升.

我们都知道, 做事情要高效, 要 WikiWiki. WikiWiki,  是每一个想要管理好时间的人的圣杯, 是每一个想多做点事情的人的魔咒; 可是很显然, 平凡的工具, 至多越用越熟; 即使用到烂熟, 也不能带来本质的效率提升的; 一成不变的思想, 最多用到极致, 形成一个自我体系, 但跳出体系外, 是不能带来崭新的角度和本质的提升的. 特别的, 是在计算机科学以及计算机编程领域, 快工具和新思想层出不穷. 依我的观察, 在计算机科学的发展史中, 每一个时代都有很多新思想涌现, 带来的是革命性的思维方法和崭新的理论实践, 以及快好几个数量级的效率提升; 在在编程方面, 我们大多数人也见识了 UNIX 管道哲学, 和函数式编程的哲学对效率的提升.  这些新思想, 好工具, 是我们计算机科学领域最好的珠玑, 也是在大海边玩耍的孩子不可错过的晶亮的贝壳.

那么, 心急的读者要问了, 到底哪些是晶亮的贝壳和藏着的珠玑呢? 别急, 我会把我见到的认为是晶亮的贝壳和珠玑的好东西记录在这里, 所以, 请继续关注我这个系列的后续文章

最后附送几个不算八卦的八卦, 算是本文花絮:

1. 本文中间那句拗口的中文是翻译自 Software Tools 中的一句: < Many jobs will not get done at all unless they can be done quickly.> 里面还有 “We consider people cost a great deal more than machines, and the disparity will increase in the future” 以及 “The extra freedom permitted by got’s is just what you don’t want” 等道理深刻的话语.  有时候我真的怀疑, Software Tools 是一本讲哲学的书, 而不是一本编程书.

2. Wiki 最早的思想来自于苹果机上的 hypertalk. 这个软件相当于是个人多媒体 Wikipedia. 苹果的 Applescript 自然语言编程的语法, 也是借鉴的这个软件的语言, 叫做 hypertalk. 这个软件称得上是个人计算机的 killer app, 但是不幸被苹果收购之后就中断开发了.  Steve Jobs 这家伙很没文化, 收购并扼杀了很多苹果上的经典软件, 这些故事等以后有空细说.

3. 关于汽车之比人快10倍, 但是本质上改变了人的生活的例子是借用的 Knuth 的, 具体可见 <Mathematics and Computer Science: Coping with Finiteness>, 文章发在 1976 年的 Science. 这是一篇好文章!

4. Ward Cunningham 的网站, http://c2.com/cgi/wiki 是历史上最早的 wiki 百科, 只是全是关于计算机和编程的而已.
10#
 楼主| 发表于 2010-5-24 15:36:06 | 只看该作者

编程珠玑番外篇-A.P2P客户端的策略和奇妙的对策论-1

原文: [url=http://blog.youxu.info/2008/12/31/tit-for-tac-and-p2p-software/]http://blog.youxu.info/2008/12/31/tit-for-tac-and-p2p-software/

最近日本著名演员饭岛老师去世了. 在我这个年龄段的人中, 熟悉饭岛老师的相信十有八九都是通过奇妙的叫做 bt 或者 电驴 的软件认识的. 今天我们就来八卦一下程序设计人员是如何设计这些客户端的策略使得您既能下载欣赏到饭岛老师的片子, 又不会浪费您太多的上传带宽的. 简单的说, 就是 P2P 软件的客户端的策略该如何设计, 使得整个系统能够帮助每个用户获得相应的利益最大化.

要研究这个问题, 我们得从博弈论谈起. 但是因为这个是给程序员看的八卦, 不是数学专业课, 我们不在这里说太多的数学, 而是用例子和八卦引入.

大家都知道, 1994 年的诺贝尔经济学奖给了一个数学家, 约翰.纳什 (电影”美丽心灵”为证). 纳什的理论工作是推广了冯诺伊曼开创的极大极小定理(博弈论的基本定理). 而在通俗的对博弈论的介绍中, 提到纳什, 一般都是着重在纳什均衡和囚徒困境上. 我们不具体深究纳什均衡的数学意义, 而是以下面一个具体的极其简化的例子来说明囚徒困境:
假设 BT 网络中两个节点 阿强(A) 和 B哥(B) 要交换文件. 文件很大, 我们假设需要非常多轮交换才能完成. 每一轮, 每个节点可以选择 平衡上传/下载 和 几乎不上传/贪婪下载两组策略. 我们按照博弈论的一般用语, 把第一种策略称为 C(合作), 第二种称为 D(叛变). 同时, 假设A, B 都是使用 ADSL 网络, 所以上传成本比下载成本要高很多, 我们在计算回报的时候考虑这样的不对称. 现在, 假设 A 和 B 各自有对方需要的文件, 那么, 如果 A, B 同时选择策略 C, 即平衡的上传和下载, 他们得到的回报都是 3, 如果其中一个人偷鸡选择 D, 即几乎不上传, 光下载; 而另一个节点选择 C, 则选择 D 的能够下载到所要的文件且几乎不需要付出上传的代价, 我们记回报为 5, 而另一个人付出了上传的费用, 却得到了一点点的下载, 可以把回报看成是0. 如果两个人都选择贪婪下载, 几乎不上传, 那么两个人都得到了一点点下载, 现在这样的下载量没有3多, 但是因为本身付出的上传成本也少, 我们把这时候两者的回报都定为 1.

说了这么多, 只是为了让问题更加的真实. 这些交代的条件的数学本质, 可用表格表示, 博弈论中称之为支付矩阵:
C D
C (3,3) (0, 5)
D (5,0) (1, 1)

现在的问题是, 阿强和B哥都是理性的, 也是自私的, 因此, 他们都认为, “假如我选 C, 对方可能选 C 或者 D, 那么我这个策略最糟糕的情况下收益是 0, 而假如我选 D, 最糟糕的情况下收益是 1″ 那么, 因为 D 下最糟糕的收益比 C 最糟糕的情况下收益要大, 理智的人肯定选D. 我们看到, 两者选择 D 都是理性的, 但是实际上从对两者的收益分析看, 两者都选择 C 才是更加优的. 这个表面上看上去很理智但是最后没有到达对双方最好的结果的困境, 就是所谓的囚徒困境. (看过这篇八卦, 您也可以叫做饭岛老师困境)
关于囚徒/饭岛困境的简单介绍就到这里, 现在我们看我们的原始问题. 我们知道, BT 交换文件是分成一块一块的, 也就是说, 是一次一次的交换的. 我们把每次交换叫做一轮的话, 整个系统是一个多轮的博弈问题(或者叫做多阶段的博弈问题). 这个博弈问题, 就显得好玩起来了. 为什么呢, 因为多阶段博弈, 居然能够让自私的A和B两个节点为了自己的利益, 进化出合作来.

我们先简单的说明一下多阶段博弈不必然的能跳出囚徒困境. 比方说, 如果 A 和 B 知道一共有 N 轮博弈, 那么最后一轮, 理智的他们肯定都陷入了囚徒困境, 在第 N 轮 的策略清楚之后, N 的问题就转化为 N-1 轮的问题. 所以, 必然的, A 和 B 在所有 N 轮上, 都会陷入囚徒困境 (好比奸商一辈子只和你做有限次买卖的话, 就会一直黑你, 不黑白不黑). 他们等到花儿也谢了, 也不能得到自己想要的内容. 但是, 问题的奥妙在于, 假如A 和 B 不知道一共多少轮, 或者有无限轮呢? 假如阿强在某轮选择平衡的上传和下载(C), 则可能正好碰上 B 哥 也选择”友好合作”, 那么, 两个人都舒舒服服的交换了饭岛老师的片片. 所以, 对于一个设计良好的BT客户端, 问题的关键在于怎么选择自己的策略, 使得既能完成自己自私的下片目标, 又能注意和其他客户端良好的合作使得自己的收益最大, 而不在于在特定的一轮中自己的得失.

这里, 我们的目标是设计一个良好的策略. 通常, 在设计一个实践中性能良好的算法的时候, 数学家和计算机科学家在这里的方法就鲜明的分野了. 数学家, 会证明这样算法的存在性, 性能上下界, 和众多的必要条件, 以及算法之间在最理想的情况下的好坏比较. 而计算机科学家, 会像搭积木一样, 用不同的基本模块, 直接尝试不同的组合, 一一做实验, 看哪种方法最好. 在这里, 我仅介绍一种计算机科学家的方法: 通过让不同方法比赛, 取出赢家, 赢家的方法最好的方法. 其实准确的说, 这个就是达尔文的适者生存的方法. 而这个比赛本身又是一段非常有趣的八卦, 因此我着重花笔墨介绍一下.

在心理学和行为学领域, 有一本非常著名的书, 叫做<合作的进化>. 其作者, 记载了在80年代, 他组织的两次比赛, 叫做IPD (Iterative Prisoner’s Dilemma, 多轮囚徒困境). 竞赛的目的是在一个多轮的囚徒困境中找出最好的策略, 参赛者自己写好算法程序, 然后由组织者让这些程序两两对弈, 看谁在多轮囚徒困境中得到最多的分. 在所有的数学家计算机科学家等提交的很多程序中, 表现最好的一个策略, 超乎寻常的只有四行简单的 Basic 程序. 这四行 Basic 程序, 勾勒出了一个叫做 “针锋相对” 的算法(Tit for Tat).  这个算法策略很简单, 一开始采用合作, 假如对方上一轮合作, 则本轮合作. 如果对方上一轮对抗, 则本轮对抗. 用中国人熟悉的话说, 叫做”人不犯我, 我不犯人; 人若犯我, 我必犯人”. (四句话正好对应四行程序, 不是巧合). 其他的算法, 比如随机算法呀, 永远敌对的算法呀, 都比不过这个算法. 因此, 这个算法赢得了第一年的竞赛.

第二次, 各位吸取教训, 继续开发好算法. 猜猜第二次谁赢了? 居然还是那四行程序! 在合作的进化中, 作者从”宽容, 以牙还牙”等社会学的角度去解释为啥这四行程序会赢. 或许对人生有深刻思考的人会感叹, 这四行程序的确蕴含了深刻的智慧. 但是, 很不幸的是, 这个程序在现实中, 有一个非常大的漏洞, 而因为这个漏洞, 使得BT程序如果不修改策略, 先现实中会寸步难行. 这个看上去非常理智非常聪明的策略到底是怎样的大漏洞呢, 我先卖个关子, 下回分解.

(想看剧透的, 可以看 Wikipedia 的条目: Tit for Tat: [url=http://en.wikipedia.org/wiki/Tit_for_Tat]http://en.wikipedia.org/wiki/Tit_for_Tat )
您需要登录后才可以回帖 登录 | 注-册

本版积分规则

小黑屋|手机版|Archiver|数码鹭岛 ( 闽ICP备20006246号 )  

counter

GMT+8, 2025-12-3 23:50 , Processed in 0.088233 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表