2009-08-28

第一台计算机的荣誉应该归美国

第一台计算机的荣誉应该归美国,但是,英国人老是不服气这一点,因为他们认为实际上第一台确实是他们发明的,是他们根据图灵的设想创造出来的(1),并在解密工作上起了非常大的作用。二战时期,德国的Enigma-machine ciphers埃尼格玛密码是波兰人初步破解(2),而在图灵的带领下,被英国人彻底破解的(3)。

但是,英国人自己害死了图灵,他们对自己发明的计算机严格保密,甚至在不需要破解密码后,销毁了图纸和机器,只有两台做为收藏之用。

而美国的第一台计算机ENIAC(4),刚开始当然也是为了军用(5),可是战后,美国人没有束之高阁,或者干脆学英国人销毁了事。后来的计算机应该是参考了它而发展出来的。从而也催生了,美国整个国家的信息化,以及现在整个世界的信息化。

所以,英国人活该。图灵当时就应该移民去美国,这样信息产业的面目也许完全不同。要知道图灵死的时候才42岁,对科学家这个职业来说,这简直就是青壮年,正是应该大展宏图的时候。

当然,这也是我认为每个中国人,我和你迟早应该移民美国的原因之一

---
  1. 应该是指Tunny Machine,为了解决出现在埃尼格玛密码之后的Lorenz cipher洛伦茨码而设计的,据说是依据图灵机思想设计的,故可称为最早的计算机,但也有人说它不可编程,故而不是真正的计算机,待考。
  2. 波兰版本的Bomba,可以解决原始的埃尼格玛密码,但是对改进后的无能为力,但是对英国人的最终破解启发极大,所以图灵的机器名字也叫Bomb。
  3. Turing–Welchman bombe,英国人造的,彻底解决了埃尼格玛密码。
  4. 按照美国法院的判决,第一台计算机是ABC,基于类似的原因,我对此不以为然。
  5. ENIAC设计的目的是用来计算火炮的投弹曲线表,实际上的第一次使用是氢弹设计中的计算。



2009-08-21

跟朋友聊敏捷(我对结对编程、单元测试、重构的看法)

大晚上的有个朋友也没睡,在Gtalk上找我聊敏捷,一聊就聊了一个多小时,谁叫我是话唠呢?我对敏捷的了解并不深,也许说的有很多不对的,不过下面这些东西都是我的一些思考,所以,贴出来就是露怯,我也不刻意藏拙了。

11:42 PM 朋友: 老兄,怎么看到现在,说的很热闹的敏捷开发方法
像ThoughtWorks这种公司
11:43 PM Tinyfool: 我接触的不多,方法确实很好
11:44 PM 朋友: 所谓的敏捷开发,主要是为了解决软件开发哪些问题?
Tinyfool: 其实还是软件工程问题
就是几个点
多人怎么协作
程序的质量怎么保证
11:45 PM 软件项目怎么组织,怎么计划
应该就是这些老问题
11:47 PM 朋友: 恩,这些老问题,其实不只是软件开发,好像只要是多人协作的工作,都会遇到
敏捷方法,能解决哇
还是,在探索过程中
Tinyfool: 大多数问题都还是可以解决的
11:48 PM 朋友: 就是说,已经有了一些好的经验和模型?
Tinyfool: 只不过有的时候在于你实施的怎么样
或者说,很多中软件工程的方法不是解决不了问题,而是实施不下去
11:49 PM 你用C写程序,写错了,编译器就不给你编译
但是软件工程是个管理问题,人是不确定的动物
我对敏捷的认识大概是这样的
11:50 PM 朋友: 很有道理
Tinyfool: 传统的软件工程方法不是不好
但是往往很笨重
或者说问题的前提很笨重
比如,人月神话,里面的前提,是历史上最大的一个项目之一
11:51 PM system 360
但是现在的开发团队,往往都没有那么大
或者说,我觉得应该不要太大
敏捷可能说,方法轻量级一些
所以更加的灵活一些
11:53 PM 朋友: 学习:)
Tinyfool: 我对敏捷了解的也不多,叶公好龙一下而已
11:54 PM 某些方法我们在用
或者想用
更多只是了解了一下而已
朋友: 哦
从不用敏捷方法,到用,中间会遇到什么需要改变的吗?
或者比较难过渡的地方吗
11:55 PM Tinyfool: 相对来说敏捷应该算好实施的吧
11:56 PM 朋友: 这敏捷倒是个美妙的概念
Tinyfool: 恩,不过也有些问题
就是很多概念看着很简单
朋友: 实施了敏捷,但是还出了问题,也可以把责任推到人身上
哈哈
Tinyfool: 但是真正要做好,其实也不是那么简单的
呵呵
你这么说也可以
我给你举个最简单的例子
11:57 PM 敏捷有一个方法叫做结对编程
简单说起来,这个应该是最好实施的
朋友: 恩,倒是听说,我一直觉得这个东西很诡异
一个哥们在旁边看着,干啥啊
Tinyfool: 就是说两个人写一个程序,不是说以前那种你写一段,我写一段,各自坐在座位上面写
对,一个写一个看,就叫结对
11:58 PM 简单说起来,你会觉得这个太好实施了
但是其实不然
这个东西的好处其实很多
我先给你说说好处吧
1、可以增进沟通
11:59 PM 在公司里面,号称是做一个工作,大家都写一个程序,或者是都做一个网站
但是也经常完全没有沟通
相互之间,可能会做出来重复的工作,或者是做出相反的工作来
12:00 AM 结对呢,代码是一起写的,两个人思路可以比较紧密的联系起来
2、避免错误
12:01 AM 人的思维,容易出错,但是自己很难发现。比如,很多人说他把一个问题想明白了,你让他描述一遍,他立刻就会发现他的逻辑漏洞百出
但是他自己想的时候,就会觉得全都是想得通的。
12:02 AM 结对,就给了你一个机会,每一步思路写成代码,旁边有另外一个人用他的脑子去理解你的代码。就像是有人听你讲一遍你的逻辑一样。
这样,你也很容易发现你的逻辑漏洞
如果你发现不了,他可能会发现,因为两个人很难同时犯一样的错误,几率太小。
12:03 AM 3、可以带新人
等等
但是实施起来,却没有那么简单
比如,两个人怎么做,是不是双屏,有时候都是实施成功的关键
12:04 AM 如果旁观者,没有办法快速清楚的看到,写代码的人的代码,那也没啥用处
朋友: 恩,我觉得还有意见不一致,到底听谁的啊
12:05 AM Tinyfool: 或者,就算客观条件,你都具备了。可以项目经理总是会觉得两个人浪费在一个代码上,是一种巨大的浪费。
虽然他知道,如果真的两人结对的话,往往可以会效率更高。但是他往往不敢放心的去这么做。
朋友: 恩,我也觉得,成本太高,经济危机下,肯定会咔嚓掉坐在边上的一个
Tinyfool: “恩,我觉得还有意见不一致,到底听谁的啊”这个好办,如果出现了不一致的意见反而是好处,呵呵
12:06 AM 这说明找到问题了
其实成本是低的
但是,从人的直觉上觉得成本是高的。
12:07 AM 你现在貌似多余的放了一个人在那里,但是这能大大减少低级错误,和逻辑错误,维护成本会低多了。
但是人们往往会根据直觉行事。
朋友: 这么说对
Tinyfool: 这也是另外一个敏捷方法
单元测试,很多团队没有办法实施的原因
单元测试,也有点反直觉
12:08 AM 传统的测试,是程序员写好程序以后,提交给质量控制的小组,测试人员写测试代码,或者人工测试
这个很符合直觉
但是其实弊端很多
测试人员是黑盒测试
12:09 AM 不知道实现方式,所以有很多典型的问题,从代码一望而知的问题
他们如果没有经验就完全猜不到
所以就是撞大运
这也是为什么有编程经验的人做测试,往往水平会比较高的原因
他知道程序员常见的一切错误
12:10 AM 比如边界问题,代码在最大最小值容易出错,输入0容易出错等等
还有管理方面的问题
我以前在金远见的时候,同宿舍的人是搞测试的
12:11 AM 他就告诉我,产品有一测二测三测,终测
测试组的老油条,经常在一测二测三测发现了小问题迅速报告
12:12 AM 发现了大问题就不说
朋友: 啊
Tinyfool: 等到终测的时候,再报告
因为,如果一测二测三测都没问题,测试组奖金就少
但是一测二测三测出现bug的奖金没有终测的高
12:13 AM 所以他们会留到终测,这样可以拿到超额的奖金
这对开发组来说就很被动
朋友: 。。。。。。。。。。。
Tinyfool: 最后终测有些大bug根本没时间好好改了
12:14 AM 只能延期,或者是简单的绕过去,看看能不能骗过测试组
朋友: 呵呵
Tinyfool: 单元测试代替不了传统测试
但是他是程序员做的
而且按照标准的流程,也就是测试驱动的原则
12:15 AM 你要先写单元测试
然后写程序
比如,你要写一个算法,实现100以内的数字×2这个功能
12:16 AM 你要先写一个测试样例,for循环1到100,然后×2,看看等不等于他们的2倍。
写完了这个测试样例,你才能去写实现。
写实现的时候,你要先写一个错误的实现
比如,不管输入什么,都返回0,或者-1
这样执行一下测试样例
得到失败的结果
12:17 AM 这才完成了第一部
然后,你才应该去写正确的实现
朋友: 恩,这个太繁琐了
Tinyfool: 写好了,执行一下测试样例,对了,你的代码才算写完了
对,确实太繁琐了
所以,以至于我是很认同这个原则的
但是我很少这么写
12:18 AM 但是这个东西非常有用
对保证质量作用极大
程序员写代码经常会写出来那种
你随便用两个数字测试一下,他是对的
12:19 AM 但是用另外几个数字测试就会错的代码
这种就很难自查出来
单元测试就是要杜绝这种问题
这是潜层次的
还有更深层次的问题
程序大了以后的问题
12:20 AM 这就涉及到了敏捷的思想的综合使用了
写程序之所以需要软件工程,其实差不多就是三件事情
我前面说了
协作怎么协作
质量怎么保证
怎么维护
维护是最大的问题
12:21 AM 经常是
维护,也不是像一般人想的,只有软件卖出去了才有维护的问题
很多软件要写几个月到几年
而你昨天写的代码,你今天就可能全忘光了
12:22 AM 所以,往往程序还没写完,维护问题就出现了
举个最简单的维护的例子
比如,你有一个算法是算工资的
朋友: 恩
Tinyfool: 很简单,就是 日平均工资×21
12:23 AM 你就写 s = c * 21
这个多简单
但是你这个系统很复杂
到处都需要算工资
你就把这句话到处粘来粘去
代码中有几百个这句话
12:24 AM 有时候,你需要算工资减去保险
那么就有了 s1 = c*21 - w
好了
你觉得没什么大问题吧
现在出问题了
老板说了,我们以后按照22天算工作日
12:25 AM 我这里还是简化了问题,有公司按照每月实际工作日算工资的
你就需要在代码中,把所有的这种21都变成22
朋友: 恩,有道理
Tinyfool: 你不能用查找替换
因为别的算法里面也有21这个数字
12:26 AM 你替换了别的算法就错了
这个s = c * 21直接替换s = c * 22,也不行
因为你有s1 = c*21 - w形式的
还有空格多了少了
就没办法了
这就是我们程序员说的代码有坏味道
12:27 AM 想不坏很容易
你把算工资写成一个函数
function s(c){ return c*21;}
老板说了,21改成22
你不用查找替换,也不用改100处
你该函数里面就可以了
对吧
12:28 AM 朋友: 恩,是的
Tinyfool: 这其实就是最简单的抽象
我的原则是,代码重复超过两遍
就要函数化
因为它就有了重复100遍的趋势
12:29 AM 随着你的工程的变大,很可能就会100遍
就像人类说名字,第一遍是全名
第二遍再提这个人,就用代词了
这就是抽象了一下
12:30 AM 朋友: 这个比喻好啊
Tinyfool: 软件是活的
你一开始写s=c*21;没有坏味道
因为他只有一遍
到处都用味道立刻就坏了
所以,你要随着项目的增长不停地去改善你的代码
12:31 AM 这其实叫重构,也是敏捷方法之一
重构是很多人心向往之,但是不敢为之的事情
一方面是很多的项目经理,觉得你在浪费时间
因为抽构完的代码味道虽然好,但是功能没有任何的改变
12:32 AM 另外一个方面,是怕你把程序改错了
第二个方面最重要
因为按照先贤的统计
你修正一个bug,就会引入4个bug
也就是说改代码总是危险的
尤其是,代码有可能不是你写的
12:33 AM 或者说,虽然是你写的
但是你已经不是你了
你已经是几十天以后的你了
你都忘了,当时这个函数是做啥的
注意事项是什么
12:34 AM 还有,本来一个程序已经对了,经过了测试组的测试
然后你什么新功能都没出来,就去再测试一次
老板和项目经理都不会喜欢你的
这时候
12:35 AM 单元测试的好处来了
单元测试是重构可以进行的保障
真正的重构有两种保障
第一,是IDE和其他工具的机制保障
这个说起来有点深了
最早重构这本书的意义就在与此
12:36 AM 人人都有自发的重构的思想
那么干嘛要这么书呢?
这本书,把重构方法论化,提出了重构的一些具体方法
这些方法都是非常具有操作性的
这就提升到了理论的高度
12:37 AM 这样,很多现代IDE就集成了这些重构方法的功能
这样,你就不会手工去重构
朋友: 我得好好读读你说的东西,对明儿的访谈特有帮助,
Tinyfool: 举个最简单的例子,你有个函数名字起的不好
12:38 AM 你要改掉,最简单的就是查找替换
但是这里有个问题
如果字符串里面提到了函数的名字怎么办?
只是凑巧一致而已
这就错误的替换了
12:39 AM 而IDE的重构功能里面的函数改名,就可以保证只替换函数声明实现,以及函数被调用的地方
不会改掉凑巧一样的字符串或者是其他的东西
这是重构正确的第一个保障
第二个就是单元测试
因为有了单元测试
所以,你可以放心大胆的修改函数的实现细节
12:40 AM 只要你修改后的函数还可以通过单元测试就可以了
像现在著名的一些开源软件
常常有数万个单元测试
12:41 AM 可以保障,不管你修改代码中的任意部分,都可以确保,你修改过的代码和以前的代码功能一致
而对于这种大型软件
修改以后,人工去核对功能是否一直,简直就是不可能完成的任务
前些日子,我帮人做一个词典软件
有个小bug
查某些词的结果不对
12:42 AM 这就很难办了
不是全不对,某些词又对
所以,最后我的做法,就是把词表倒出来做了一个5万个词的单元测试
每个词都查一边比较和预期的结果到底对不对
12:43 AM 这样找出了100个不对的
这100个不对的,其实对应了5种不同的情况
当然我事先不知道
我就先看第一个不对的
跟踪程序
找到原因
修改,然后再跑单元测试
12:44 AM 这下只有60个不对的了
就这么我其实就找到了5种情况,就保证了程序的百分百正确
如果我不做一个单元测试出来
我很难找全这5种情况
12:45 AM 我修改了一个bug以后,也不知道是不是解决了所有的问题
你理解这个例子吧?
朋友: 恩,明白了
Tinyfool: 在单元测试思想出来之前,你遇到这种
问题
是很难彻底解决的
12:46 AM 单元测试可以帮助我们开发出来几乎可以说完全没有错误的程
朋友: 哎,通过你这些例子,俺对敏捷是长进了一大块
Tinyfool: 只靠黑盒能找到我刚才那个错误中的两三个情况就不错了
12:47 AM 我的了解还是很肤浅的
Tinyfool: 哈哈,那倒是可以去看看
12:54 AM 朋友: 打扰你这么长时间,真不好意思啊
12:55 AM Tinyfool: 呵呵
别客气
我把你的名字隐去,然后发blog你不介意吧
呵呵
12:56 AM 朋友: 哈哈,这怎么会介意
我刚想说,你刚才的内容,真可以写一篇文章了
Tinyfool: 呵呵,我自己写就懒的写,跟人聊心态轻松点
朋友: 算是对敏捷的一个梳理
12:57 AM Tinyfool: 我的了解还是不够深入
朋友: 哈哈哈,那,以后,想写博客前记得找我
Tinyfool: 好像到现在为止还没看过一本全面的敏捷的书呢,呵呵
:)


后:
聊天是我最没有压力的写东西的方式,前些日子的TL聚会有人反应我一个人说了3/4的话,就是因为如此。如果是聊天,我就可以没有压力的海阔天空的说,一旦正正经经的写个文章,我就会花大量的时间去考据,查资料,以免露怯,所以我最近一两年写东西越来越少。结对编程是我最心向往之的敏捷方法,可惜一直没有好好的实践过,这反而我和霍炬倒是经常一起结对设计,呵呵。8月23日,即本周日,Beta沙龙的话题是我的合伙人霍炬来讲我们如何在应用中使用Snmp协议来进行服务监控的,欢迎大家参加。



2009-08-10

瞻仰海峡对岸的牛人《追求神乎其技的程式設計之道》

活在这个日新月异的世界是需要巨大勇气的,因为有太多牛人在侧,以前(小学、中学、大学)我的生活圈子小,见得牛人少,觉得自己就很不错了。但后来有了互联网,我脆弱的心灵就一次一次地被各色牛人冲击着。

最近几次,是从刘未鹏开始的,他组建了一个颇有气氛和深度的Google Group:TopLanguage,里面牛人无数。他自己当然是其中的佼佼者,他的Blog虽然更新不多,但是每篇都巨长具有深度,很多年以前,他大学还没毕业的时候,我看他的Blog就以为他是哪个深山归隐的老怪了。最近,他的一篇我在南大的七年》,则彻底摧毁了我残存的一点点自信,为什么牛人都那么年轻那么帅啊。未鹏的学弟是我的好网友xuyou同学,这个家伙也是牛的一塌糊涂,Cool的一塌糊涂,现在正在美国读博。他跟风未鹏的作品《我的大学》则让我相信我活着就是糟蹋粮食(按照我的体重,我这方面还算是有特长的),我准备找个山清水秀,春暖花开的日子自绝与人民。

然而,就在这时,我看到了海峡对岸的牛人Vgod(现在在MIT读书去了已经)的文章:
我突然想通了,牛人到处都是(斯坦福啊,麻省啊,都是一院子的),我还是勇敢的活下去吧。假设我还有30年好活(已经活了30年,再开30年的缓冲区,效率应该还可以),至少有4年可以用来把大学没学好的有兴趣的东西学好,再多写点自己想写的程序,改变世界很难,但是取悦自己不应该成为一个难题。

就像Zooey Deschanel在《Yes Man》里面说的:The world is a playground. You know that when you are a kid, but somewhere along the way everyone forgets it.