Posts archived in Article

5 comments

也就100年

Obama 当选总统了. 撤军, 加大对科研投入, 减税等等, 对我们这些在美国的学生是一件好事情. 

在赢得胜利的演讲中, Obama 说到了一个106岁的修女 Ann Nixon Cooper 的故事,

“She was born just a generation past slavery; a time when there were no cars on the road or planes in the sky, when someone like her couldn’t vote for two reasons — because she was a woman and because of the color of her skin. And tonight, I think about all that she’s seen throughout her century in America — the heartache and the hope; the struggle and the progress; the times we were told that we can’t, and the people who pressed on with that American creed: Yes we can. 

At a time when women’s voices were silenced and their hopes dismissed. She lived to stand up and speak out and reach for the ballot, yes we can;

When there was despair in the dust bowl, and depression across the land, she saw a nation conquer fear itself with a new deal, new jobs, a new sense of common purpose, yes we can.

When the bombs fell on our harbour and tyranny threatened the world, she was there to witness a generation rise to greatness and a democracy was saved, yes we can.

She was there for the buses in Montgomery, a bridge in Selma and a preacher from Atlanta who told the people that we shall overcome, yes we can.

A man touched down on the moon, a wall come down in Berlin, a world was connected by our own science and imagination and this year, in this election she touched her finger to a screen and cast her vote, because after 106 years in America through the best of times and the darkest of hours she knows how America can change, yes we can. ”

(她是废除奴隶制后的第一代人, 是个没有汽车和飞机的时代. 因为性别和肤色,  她没有投票权. 今晚, 我思考着她观察到的一个世纪的美国–心痛和希望; 抗争和进步; 从我们被告知我们不能改变, 到今天, “我们可以” 成为美国人的信条代代相传的信念.  

当妇女的声音不能被听到, 当希望被剥夺的年代, 她站起来, 为妇女的投票权抗争: 我们可以. 

当大萧条弥漫这个国家, 当黑风暴带来绝望的时候, 她见证了这个国家用新政, 新的工作和新的公共福利战胜恐惧. 是的, 我们能! 

当炸弹在珍珠港落下, 当暴政威胁整个世界, 她见证了一代人的伟大, 见证了民主被捍卫. 是的, 我们能! 

她见证了在蒙哥马利种族隔离的公共汽车, 见证了在民权运动的起源地, 见证了那个亚特兰大牧师(MLK)对人们说: 我们终会克服这些. 是的, 我们能!)

我最爱的小说是<百年孤独>, 一百年后的拉丁美洲和一百年比, 一场大洪水之后什么都没剩下; 而在美国, 100年, 足可以让一个没有人权保障和普遍选举权的国家, 变成一个民主自由的世界灯塔. 也就100年. 

一个国家的下一百年, 是走向百年孤独, 还是走向自由民主, 值得我们深思. 也就100年, 我们同龄的大多数人至少能见证70年, 70年过后, 回问自己: 在被别人告知不可以的时候, 你是什么也不做, 还是握紧拳头在心里和手掌心都深深的刻下: 我们可以改变. 

改变一样东西, 100年足够了. 别信什么一万年太久, 只争朝夕, 那也是谎话. 别着急, 也别绝望, 用切实的行动, 改变我们这个世纪.

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

 

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 的语言容易得到. 这时候, 我推荐 valgrind. 这个工具能够把 C/C++ 程序放到一个容器中执行, 记下每一个内存访问. 被这样的容器 debug 一下, 基本上指针指飞了 (Segmentation Fault) 的情况几乎就没有了. 想像一下是用 GDB 追踪非法指针和内存泄露方便, 还是用容器告诉你哪一个指针非法, 哪一个内存没释放方便 :)

 

2. 选用自动化工具构建

用 gcc 或者简单的 IDE 来编译和运行程序在编程初期是很快速的, 可是越到后来, 会越臃肿. 在编译的时候, 不同的参数, 不同的目标, 在 IDE/gcc 里面每次都要设定. 而且一般的 IDE 也不能做到自动解决依赖等高级方法. 因此, 最好的方法是用 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 工具. 

 

另外: 用两个或者大于两个显示器. 不要用或者少用鼠标.

clown-mask.gif我和 Junyu Wang 某天突发奇想, 做了一个讽刺汉编的项目. 我们觉得这个项目是有意义的, 至少能在娱乐的过程中让一部分志同道合的人聚齐来做件事情.  事实上, 我们也达到了这样的目的. 在写项目文档的过程中, 我们加入了很多娱乐化的东西. 让一个本来很简单的项目变得很搞. 因此,名声也传得很远. 这四天以来, 除了签名和 twitter , 我们没有任何宣传, 想不到短短几天, 很多网络媒体都报道了. 很多高校的BBS上, 很多的博客, 都说了这个项目的事情. 也获得了很多好的评价.

这个项目目前每天还有一千多人访问. 虽然这个恶搞带给别人很多”笑喷”, 我实际上是带着一丝的无奈来做这个事情的. 在中国的互联网上, 能说什么, 不能说什么, 实际上都有人给你框定好了. 你不能使用正常的语言, 自由的谈论国内一些地方发生的事情. 你所能做的, 是把整个事件符号化, 符号化成无毒无害的俯卧撑. 然后通过这个俯卧撑, 表达一下自己对公共事件的关注和无奈, 表示一下: 恩, 我知道了, 但是我没法说出口.  这样的符号化相比于以前一点也不关心是进步; 可相比较于我们要达到的目标, 还显得遥远和无奈.

可无论怎么样, 我看到了公共精神的强化. 至少带着娱乐和面具参与公共事务, 是一种明智而实际的观点表达方法. 以前不出来活动的人, 现在也愿意带上面具, 跳进舞池, 缓缓踱步了. 一场地震唤醒的民间意识, 随着打酱油, 俯卧撑这样的流行词语, 如蒲公英一样散布在各处, 一旦实际成熟就生根发芽. 这个比星星之火还要有深度和广度.  那些沉默的大多数,  如蒲公英一样, 不说话, 不喧闹,  只用俯卧撑做这个时代的见证.

baiyang.jpg我支持这样的娱乐化以及这样的表达方法.  圣雄甘地说,  You should be the change you wish to see in the world.为了改变这个世界, 从我做起是最简单的, 所以我也愿意实践这样的方法.  我只希望这个插科打诨的方法能很快被淘汰.  我至今都不忘高中的校训: “正直向上,热于求知”.  一个人在世上, 要先做正人, 然后做正学.  希望有一天, 这个国家所有的公民能够坦坦荡荡的说出自己的看法, 都可以不受任何阻碍和顾虑, 做一个正直向上的人. 那时候, 就是扔掉面具,  积极参与, 认真行使权力之时.

(本文是我长期思考得出的理论文章, 比较枯燥, 如果有兴趣请耐心看完, 没兴趣请立即关掉浏览器)

共产主义, 是卡尔*马克思所描绘的未来社会的社会制度. 这是在相对黑暗的工业革命和资本主义经济危机时期, 马克思通过对资本主义世界观察做出了结论. 我们今天回顾这样一个被创造出来假定能解放全人类的制度, 未免要问, 为何一个良好的制度, 在20世纪, 无论在哪个国家的革命实践中, 最后都无一例外走向了1984所描绘的极权主义. 是乔治*奥维尔洞穿历史的远见, 还是有什么深层次的原因在里面? 这样的问题不得不让任何一个对历史感兴趣的人思索.

如果我们从共产主义的提法入手, 就会看到一条清晰的逻辑脉络. 即从资本主义的基本矛盾, 1. 推出生产资料必须要社会占有, 2. 推出全社会必须充当生产资料的拥有者, 也就是必须要有社会主义 3. 而在当前, 社会就是由国家充当. 国家拥有所有生产资料, 干涉国民经济的方方面面, 就必然会干涉民众生活, 国防, 军队, 司法, 立法等方方面面. 生产资料是社会中唯一重要的东西, 生产资料的高度集中控制导致了权力的高度集中, 导致了极权主义.

下面我一一详细解释出现1984的必然性.

1. 马克思认为, 资本主义的基本矛盾, 是生产社会化与资本主义私人占有形式之间的矛盾. 这个矛盾是继承马克思对社会基本矛盾的思路, 即生产力和生产关系之间的矛盾提出的. 所谓的生产社会化, 就是说随着生产力的发展, 生产变得越来越需要社会部门各环节的配合. 马克思认为, 生产的社会化要求, 必然伴随的要求是生产资料的社会化要求. 这个论述很难用经济学验证. 但是按照马克思的生产力决定生产关系的理论, 这个结论是说得通的. 接着, 马克思认为私人资本和生产社会化是一对矛盾, 而且不可调和, 即私人资本永远无法协调社会化生产. 这一点却有很多反例. 我们先说什么是私人资本 (在马克思时代, 资本的归属是很清楚的, 因为股票等现代金融手段还很脆弱, 马克思对股票的定价公式还是简单的四则运算. A股股民如果学习马克思政治经济学肯定纵做鬼, 也幸福, 这是后话). 简单的说, 马克思认为, 能够全部被少数资本家控制的资本, 叫做私人资本. 相对的, 被社会所拥有的资本, 称为社会资本. 这一段我倾向于同意马克思的, 即生产资料不能被少数资本家控制, 否则会阻碍生产力发展.

2. 马克思认为, 资本家无法协调社会化大生产, 经济危机不可避免. 1933年之前所有的经济危机都验证了马克思的结论,  1933 年后, 包括战后, 世界变得很不一样, 科技革命以及国家干涉经济的策略, 很大程度上社会化了原有资本. 不少马克思主义学者认为这个缓和是无法克制本质矛盾的, 本质的还需要社会主义来解决. 什么是社会主义呢,  如果我们引用邓小平的话给社会主义的概念做一个界定, 即不断解放生产力, 发展生产力的社会制度. 那么我们就会发现, 只要能保证生产力继续发展的制度, 都是适应生产力的, 都是社会主义. 邓明显是偷换概念循环定义社会主义来给当时保守派做思想解放的. 研究马克思主义的经典著作就会发现, 社会主义就是生产资料归社会占有的制度. 马克思并没有细说如何才是归社会占有. 诸如现代股份制公司这样的比集体所有制还要集体所有制的组织究竟是不是社会主义的生产方式, 马克思却没有说. 出去对于当时存在的生产资料, 如土地, 交通资源发表过只言片语外, 马克思对生产资料归社会占有的程度, 方法, 形式, 都没有任何论述, 都是空白. 概括来说, 我对马克思提出的生产资料必须以社会占有为方式并不同意. 如果说生产力解放才是本质, 那么生产资料如何被拥有, 就是一个实践问题, 而不是一个判断问题. 实践中可能存在很多他没有想到的生产资料社会化的方式. 事实上, 的确有很多时候生产资料不以被社会占有的方式社会化. 如国家干预, 科技革命, 股权改革, 等等, 都可能是社会化资本的方式. 不一定通过占有, 才能社会化.

3. 上面的空白留给了后人解读的空间. 在苏联, 集体化的农庄代替了小农经济, 在中国, 社会主义改造完成了公私合营. 其他东欧国家道路也类似. 简单的说, 就是生产资料社会化这样一个愿望, 在实际操作中, 变成了生产资料的国家化. 这一步, 是造成极权主义的最重要的原因. 我一一详述.

首先, 以历史为例, 所有社会主义国家都走计划经济的道路并不是偶然的. 我们已经说了, 生产资料的社会化这样一个论断, 在苏联和中国, 无一例外的变成了生产资料的国家化. 这个转变是必然的, 因为没有任何一个社会群体, 能够如国家一样, 担当起社会的职务. 在共产党宣言中, 马克思认为, 这样的道路, 是向共产主义道路过渡的必然. 马克思也认为国家必然要灭亡, 至于最后的社会化到哪里去, 马克思也没有讲. 这样, 在过渡时期待多长就是很有意思的事情了. 虽然中国和苏联都曾经宣布很快就要进入共产主义, 但是实际上这一天从来没到来过. 在到共产主义之前, 生产资料的国家化, 意味着国家拥有公民的一切资源. 也就是说, 公民在国家这样的权力机器面前极度不对等. 所有的政府, 警察, 军队, 都被国家控制. 所有人, 除了”国家代言人”,  都实际上是无产阶级.

其次, 我们探究一下为什么国家控制一切的体制, 产生的不是高福利的民主社会主义, 而是无一例外的极权主义呢. 为什么所有的社会主义国家都没有北欧国家那样的福利水平和民主水平呢. 为什么良好的愿望变成了不好的结果呢? 我想, 很简单, 用绝对的权力必然产生绝对的腐败这句话概括再好不过了. 国家社会主义理想很大, 试图涵盖从饭票到花生票的方方面面. 因为国家统管一切, 权力不在阳光下运行, 即使开国时再好的愿望, 也成了一塌糊涂的专制.  那么, 为什么北欧国家能够实践民主社会主义呢. 其实也很简单, 北欧国家的政府并不是一个侵入性质的政府, 而是一个开放的几乎只管公民福利的政府. 在这样的政府下, 生产资料的私有化被高税收磨平, (所有的人从私有制企业获得福利, 就等于所有的人拥有这个企业, 和国有企业利润被国家拿走再分配一个道理), 政府再通过经济杠杆调节资本的社会化. 这样的实践, 历史证明是成功的.

那么, 马克思有没有预见到共产主义能导致极权主义呢? 或者说, 马克思有没有预见到从国家社会主义走的这条道路就是极权主义的道路呢? 我认为, 他是有预感的, 但是他相信所有的人都是共产主义的”道德高尚, 按需索取”的, 即使有独裁, 也都是仁慈的独裁者.  我们知道,  历史上第一个探索实践社会主义的, 不是苏联, 而是巴黎公社. 在巴黎公社失败后, 马克思痛心疾首的写过一个小册子, 叫做 <法兰西内战>. 在册子里, 他重点说了两个教训:

1. 巴黎公社不该浪费时间做民主选举, 应该赶快消灭敌人
2. 巴黎公社应该实行无产阶级专政, 控制法兰西银行, 使用里面的钱保卫自己的政权.

我相信这是属于有针对性地策略. 然而以小见大, 至少我们可以看到这个共产主义的创始人提出了两点: 1. 保卫自己最重要, 民主选举应该建立在政权稳定基础上; 2. 不必尊重私有财产, 应当利用一切资源保护政权. 这几百年过去了, 我想, 无产阶级的特性没有变, 一方面因为阶级的特性, 对于私人资产看得不神圣, 可以随便征用, 第二是因为随时都有被绞杀的危险, 所以随时都准备绞杀对立阶级, 因此从诞生第一天起战斗性很强. 所谓以小见大, 从共产主义的创始人这里, 我们也能看出, 为什么无产阶级领导下的一些国家, 会造成相似的问题. 马克思的阶级分析观点套在资产阶级上可以得出资本主义的各种结论, 其实套在无产阶级上, 也是一样的. 从这两句话, 也能自然的得出这个阶级的一些性质, 以及这个阶级掌握政权后采取的一些做法的必然原因.

附:

1. 本文是我长期思考的结果, 因话题较为敏感, 且请尊重我的思考劳动, 请不要转载.

2. 对言论的态度是极权主义正式建立的最好的标尺. 而一个国家机器的建立离不开经济体制的改革.  我们不妨看看这两件事情:

1929年苏联开始集体农庄改革, 到1933年1月,联共(布)中央宣布: “把分散的个体小农经济纳入社会主义大农业的轨道的历史任务已经完成”. 2000多万农户被20多万个集体农庄所取代.
1934年苏联一个领导人遇刺, 斯大林开始大作文章, 从1936到1938, 开始了著名的”大清洗” [来源于被封百科]

1953-1956 年, 中国开始了”伟大的社会主义改造”. 在1956年的第一季度末, 全国全行业公私合营的私营工业已达到99%, 私营商业达到85%, 基本上完成了对资本主义所有制的社会主义改造.
1957年, 反右斗争把大量的人民内部矛盾当作了敌我矛盾,把一些知识分子、爱国人士和党内干部错划为“右派分子”. 很多人到1981年才平反.  [来源于人民日报]

3. 经济危机是否是基本矛盾造成的仍然值得商榷. 我倾向于使用经济学的定量方法分析, 而不是抓着所谓的基本矛盾沾沾自喜, 以为发现了真理.

7 comments

因爱而信

很多年前的一个初夏, 六一, 我给一个喜欢的女孩子写情书祝她好运, 我写道: 虽不信神, 也愿祈祷.  今天我又读到一篇文章, 叫做:  从来没有像今天这样地祈盼天堂的存在. 我非常理解作者. 我多希望那些孩子全部在天堂里好好的. 其实, 大地震之后的七天里, 我每次打开网易新闻, 心里都先默念: 奇迹, 更多奇迹. 我愿意相信一个神, 给予废墟中的人更多的奇迹.

我也常常想, 像我这样不相信有神的人, 为什么还要为他人默念奇迹, 为什么要为喜欢的女孩子祈祷. 以前, 在教会朋友的询问下, 我曾经说, 我只相信物质世界(materialism) 他们总是很惊讶, 为何中国十多亿没有信仰的人, 没有变成大奸大恶之徒. 我也一度想不明白, 是否没有信仰的国度一定让有信仰的人感到悲哀, 一定会走向悲剧. 我想现在我部分知道了这些问题的答案: 我们其实有信仰, 我们因爱称信. 因为爱, 而信, 而望.

看新闻, 看那些志愿者在一线; 看同学, 朋友, 几乎每一个我认识的人都用各种方式参与抗灾; 听李连杰在北大说了几十次爱; 看很多平时政见不同的人都在一线手挽手做事情; 看高校大学生比着献血, 排长队还拿不到号码. 看唐山和各地朴实的农民工自己掏钱到前线, 做不了其他的, 就帮忙发水, 挖排水沟. 看一个个精彩的人与人之间的爱的故事, 不问来路, 不问回报, 一心付出.

我有朋友信仰耶稣, 我问为什么, 他说: 因为喜欢基督徒的那种有爱的生活方式.  虽然我逻辑上不认同因信而爱, 但我很支持他, 理解他. 如果因为信, 走到爱, 我觉得这个基督徒, 按照圣经的定义, 是一定能上天堂的. 同样, 佛教徒由信到普度众生, 也一定能成佛的.

我的书架上放了一本圣经, 翻开就是约翰书, 我从里面断章取义摘几段.

从来没有人见过神. 我们若彼此相爱, 神就住在我们里面, 爱他的心在我们里面得以完全了.
  人若说, 我爱神, 却恨他的弟兄, 就是说谎话的. 不爱他所看见的弟兄, 就不能爱没有看见的神”


你若问我信仰什么, 我信仰爱. 不是矫情, 就是信仰. 我爱父母, 以及所有的所看见的兄弟姐妹.

我一直很推崇使用 Python, 但是推崇不是迷信, 今天就说说几个 Python 相对于 Java 和其他语言的缺点, 以及初学者要注意什么.

1. 文档不完善, 最少惊奇原则不适用

很多 C++ 和 Java 开发者都知道, MSDN Java Doc 堪称技术文档的典范. Java 更是充分体现 Knuth 提出的 Literate Programming 的精华, 用一个专门的叫 javadoc 的工具自动提取程序元信息, 生成交叉引用的 JavaDoc 文档. 任何复杂的类, 框架, 在javadoc 的统一管理下, 可以生成非常漂亮的文档. 如果再配合设计模式中的 facade 模式, 一个复杂的框架很简单就可以上手. 这方面, Hibernate, Lucene 都是优秀的典范. 当然, 交叉引用的文档也有明显的缺陷, 就是极其庞大冗长, 因此 JavaDoc 和 JDK 是独立的两个部分. MSDN 更加绝了, 直接出几张光盘, 你爱装不装. 虽然冗长, 好处就是在面向对象的汪洋大海中顺着链接, 永远不会丢失. 而 Python 的文档还远远不够细致. 所有的对象方法, 只是简单介绍. 如果一个对象方法返回另外一个不熟悉的对象, 则必须手工定位新对象的位置, 而不能顺着文档继续前进. 为了搞清楚一个包, 完成一个小任务, 可能要多次搜索文档. 对于黑客级别的用户来说, Python 文档言简意赅, 喜闻乐见, 只要记住了每个对象的每个函数, 写程序如行云流水. 对于初学者和用 Python 写稍大项目的人来说, 这就是最恐怖的噩梦. 因为以介绍单个函数功能为组织方式的文档, 对于刚上手的用户来说, 很难顺着对象调用关系,通过文档指引完成任务. 即使著名的框架比如 django, 也缺少交叉引用的细致的文档. Python Library Reference 只能说是一个完成了 50% 的README. 在这种情况下, 除非对一个框架很熟悉, 否则这个框架对于初学者来说简直是”惊奇的噩梦” — 一般用户只能看例子然后顿悟到: 哦, 原来是这样用的.

除此以外, 因为语言的动态性, 使得 IDE 能给的帮助特别少. 对于 Java 程序员来说, 很多时候都是靠着 Eclipse 的提示去选择正确的方法调用. 而因为 Python 的动态性, 变量的类型到运行时才能确定, 因此能够给的提示相对变少. Python 既是动态的, 又是强类型的, 所以代码的阅读者必须要一步一步手工追踪中间对象的类型, 并且查阅对象方法文档, 才能搞清楚到底一段代码做得什么. 而这一切, 偏偏是没有 IDE 帮助的. 对于开发者来说, 如果不熟悉对象的可用方法, 用 Python 开发也会事倍功半, 因为要常常停下来去 Google.

当然, 文档不够完美, IDE 不支持的现状, 根本是因为 Python 的动态性: 谁让 Python 返回值不规定类型呢? 我的建议是, A. 对于现成已知的一些对象方法上, Python 社区应该注意文档的维护. B. 对于不熟悉对象的可用方法这个问题, 要买一本 Pocket Reference. 或者下载一些 cheet sheet. 在刚接触 Python 或者 刚接触 Python 框架的时候, 一本薄薄的可供快速查阅所有对象的所有方法的小书, 无比重要. 这也间接的说明, 和 shell 脚本, 正则表达式语法等一样, 脚本语言必须要很熟悉才能发挥魔力.

(– 附实例,对Java 不熟悉的可直接跳过: 我曾经做过一个游戏, 这个游戏类似于推箱子, 其中的一个核心是要把多幅带alpha 通道的图像拼成一个图像, 然后显示在窗口中. 打开 API Doc, 简单的搜索一下, 发现有 java.awt.Image 类. 不过这个是抽象类, 没关系, 文档下面就明明白白写着两个可以直接用的类, 一个是 BufferedImage, 另一个是 VolatileImage. 看上去 BufferedImage 更像, 于是点进去, 正是想要的. 看看构造函数, 一个空白的图像就构造好了. 顺着方法向下看, 看到 createGraphics() 方法, 返回 Graphics2D 方法, 说明中写着这个可用做在 Image 上绘图, 正是想要的, 点返回值, 是 Graphics2D, 有方法叫 drawImage. 任务完成, 轻松自然.

回到 Python. 我遇到过一个需求, 是用 Python 提取所有一个 Zip 文件的注释. Google Zip+Python 定位到 zipfile 模块. 找到 ZipFile 对象. 读文档没发现可以往外读注释, 只能碰运气, 开一个解释器尝试. 看上去 getinfo 可以. 因为没有交叉引用, 只能搜索ZipInfo 这个对象. 终于看到一个方法是 comment. 不知道类型, 仍然需要自己在解释器下先 type 测试一下, 才能在程序中写下一行完整的语句.–)


2. 文档上有, 但是你不能用

我用 Ubuntu/MacOSX系统, Python 是自带的. 以为万事大吉, 实际上, 因为 Python 所有的库都是运行时装载的, 所以有些库在有些系统上是缺失的, 而文档则是所有标准安装的超集. 因此即使文档上有, 你也没法用.

这个问题对于 Java 程序员和 .Net 程序员来说是不能接受的. 对于任何一个良好设计的 SDK, 文档和实际可用的应该具有同等的覆盖面. 在 Java 中, 有一个包就是有, 没有就是没有, 绝对不会出现标准文档中提到了, 实际中却不可用的情况. 对于可选包, 用户也能理解需要下载模块. 而对于 Python, 开发人员很难理解为什么标准文档中提到了, 实际中却缺这个组件缺那个组件. 一个潜在的伤害就是, Python 的跨平台能力没那么强. 试想, 开发的机器上有一个模块, 而用户没有. 而标准文档偏偏又认为这个是一个标准组件, 那么, 在装载的时候,找不到这个组件了, 让用户根本就无从下手. 我的建议是 A. Python 应当有更好的 import 报错机制, 对于不是每台机器都安装的模块, 应当给出平台中立的更加友好的帮助信息, 而不是简单的 ImportError. B. 应当在 Python 语言中支持更加强大的内省机制, 对于系统当前可用的包, 应当有一个全局的缓存. 这样, 用户才能知道哪些可用哪些不可用, 不可用的时候怎么办. C. 对于用户来说, 只能求助于Google 或新闻组, 或者从 Python 源代码中找线索. 一个较好的办法是在 Python 源代码上 执行 make test, Python 会汇报哪些模块已经编译, 哪些模块被跳过了.

(–实例: 前几天想写个程序抓Google Reader. 认证的时候要用到 SSL. 因此找到 Python 文档, 照着例子 import urllib2, 然后直接连 Https 服务器. 出错了, 原因是没有 ssl 支持, 不能访问 https. 所有的 Python 在线文档都说, 这个是标准模块. 对呀, Python Library Reference 里面都有的模块, 怎么能有问题呢? Google 搜索也帮不了什么忙, 因为 Python 是跨平台的, 不同的平台有不同的解决方法. 所有的在线帮助只能提示到 _ssl.o 这一层. 于是, 只能翻看 Python 源代码, 在 Modules/_ssl.c 中看了一圈, 发现需要 openssl/ssl.h 等文件, 这才意识到, 需要安装 openssl 和 libssl-dev. 在 Ubuntu 上, 同样的问题还存在于 sqlite3 模块, tk 模块 和 bz2 模块. 分别需要安装 libsqlite3-dev, tk-dev 和 libbz2-dev, 再重新编译 Python 系统. 使用 make; python setup.py install 来安装 –)

3. Python 是胶水语言, 用是简单的, 模块开发是痛苦的

Python 用到一定程度以后, 一个自然的需求就是把其他语言, 特别是C语言写的库包装成 Python 库. 这个问题说复杂也不复杂, 在 Python/C Reference Manual 中也提到了所有必要的信息. 只是层次太细. Boost.Python 有更加好的实现, 不过需要对 C++ 模板很熟悉才行. 当然, Python 的模块开发的痛苦比起 JNI 复杂的 JNIEnv 对象, Lua 的 基于栈的 Push Pop 已经是大大轻松了. 相比较于 Ruby 的自动生成 makefile, 也算各有千秋. 只是模块开发设计到的技术细节太多, 要对 Python/C API 了然于胸才行. 因此我的建议是: 不要从轮子造起. 如果有这样的模块, 就用. 如果没有, 而又非要用特定功能的模块不可, 那就暂时先不要考虑用 Python. 相比较于其他更加成熟的语言, 比如 Java, Python 的可用模块还是显得少了一点. 因为 Jython 的出现, 这个情况已经大大缓解. 不过工业界历来喜欢用最稳定的东西, 所以可以想象, 多少 Web2.0 公司是为了用 Lucene 才用 Java 服务器的, 而每天又有多少新的公司投入 Java 的怀抱 :)

某午夜, 灵感泉涌, 找灯开关, 找笔, 找纸, 找合适的词, 试图再现思维的火花. 提笔, 乱七八糟的中文, 潦草的数学符号、记号, 几行伪代码, 夹杂英文单词. 我常常想, 假如我从来不曾学过数学, 从来不会英语, 我记下的思维状态, 是否会和现在一样; 我在思考的时候, 会不会自觉的使用数学语言, 会不会用英语? 咿呀学语的婴孩, 不会使用人类语言的聋哑人, 又该如何记下他们的思维? 语言符号到底是构建了人类的思维, 还是限制了思维的表达?

谱曲要入五音, 写词要从平仄; 语言要有语法, 文章要有结构; 这些, 包括编程, 计算, 证明, 都是在使用思维而赋之于符号规则系统(语言). 符号系统有其规则和用法. 守其规则, 才能有交流. 如果放弃了语言的交流功能, 则可以解放思维, 自由表达. 如果我们观察乡村里的”土哑巴”(不会正规的手语,手语是自创), 则只有周围的少数人能理解其含义. 从他的思维层到自创的手语层也许是透明的一一对应的, 无碍的. 但出了他的手语层而要求和他人交流时, 其自创的手语又成了交流的障碍. 因此, 规则是用以交流用的语言必须内涵的. “即使狮子会说话,我们也不懂得它”.

语言能力不是与生俱来的. 即使完全通过逻辑, 从原始的事实构建到高层的抽象, 也需要时间. 以数学为例, 从最简单的集合论和代数系统到费马大定理, 完全通透的走过也需要比较长的时间. 而构建这个大厦的砖块只是几条公理, 建造的方法又只有逻辑一种. Wittgenstein (维特根斯坦) 在逻辑哲学论中讲语言是思维逻辑结构的几何投影. 思维逻辑结构建立后, 可以借助于语言, 通过一个人传递到另一个人. 如果语言的几何投影能够被简单还原, 思维就通过语言完成了一次传递. 在A(创造者)脑中, 语言是被构建的. 在B(学习者) 脑中, 语言是被解构并还原为事实的逻辑图像(思想)的. 在学习者那里, 思维是要需要借助语言进行的.

一个人认识到的世界不是完备的, 因此使用的语言集合也只是一个子集. 对语言的理解程度决定于对世界的构建. 仍以数学为例, 研究微分方程的数学家与研究代数数论的数学家如果遇到了, 可能彼此都不懂对方在讲什么. 微分方程世界的语言, 对于代数数论世界的数学家, 是难以解构的, 因为他的世界里缺少语言反向解构的对应物. 回到自然语言, 英语对于很多中国人, 是无法解构的, 因为我们无法分辨这个语素到底对应了什么. 但是假使我有一本字典, 这个问题也就解决了. 抽象的层次, 表达的符号, 描述的世界等所有的一切不同, 都让语言这个逻辑图像呈现出了不同的样子, 而让不熟悉这个语言系统, 或者缺乏逻辑世界对应物的人, 云里雾里.

我们来研究一个设计精巧而简单的语言: 程序设计语言. 程序设计语言是也是表达思维的工具.

程序设计语言有语法, 有规则. 语法和规则是为了让底层的语言理解机制消歧义, 获得一致性的解释. 符号在程序设计语言中发挥了原子的作用. 几乎所有的编程语言都是基于封闭世界假设, 也就是说, 没有声明(被计算机感知)的原子符号, 不能使用, 每个事物都有其本体论含义. 编程语言把真实世界的逻辑关系映射到了计算机世界. 因此受制于计算机世界. 在语言和思维关系当中, 语言的限制造成思维表达能力相对受制, 而相反的, 交流能力却大大提高 (居然能和一台机器打交道了).

图灵完全性从一定程度上解决了语言能力的问题. 一般来说, 编程语言被计算机解构后, 描述计算机世界的能力是相同的. 但是这不代表编程语言的表达真实世界的能力是一样的. 事实上, 不同的编程语言的描述世界的能力是不一样的.

程序设计语言是真实世界的逻辑图像, 这个图像对应了一种逻辑(算法), 一个设计哲学(模式, API)或者一个世界模型(框架). 而这个图像可能位于世界的不同层次. 以Java, C 和 SQL 为例. Java 是对象世界的逻辑图像. C 是过程世界的逻辑抽象. SQL 是关系(数据库)世界的逻辑抽象. 因为世界难以”一言以蔽之”, 因此不同的语言出现是必然的. 三个语言在解题能力上是等价的, 然而, 之所以没有人用 Java 用做关系数据库的语言, 是因为 Java 的逻辑抽象, 不是关系数据库的一个直接几何投影. 因此, 用 Java 这个逻辑图像去重现施加于关系数据库世界的逻辑, 是不够直接简明的, 至少说, 不是最贴切的.

从程序设计语言的使用上, 我们可以清楚的看到, 语言的错误选择限制了思维. 语言的错误选择不在于语言本身, 而在于语言和表达世界的不契合. 以 Java 编程为例, 一开始的 public static void main 封装在一个 public class 中, 不知道吓退了多少从来没有接触过面向对象的语言学习者. 推而广之, 即使一个框架设计再好, 不了解其设计哲学, 仍然觉得难学难用. 不契合来自于四方面, 1. 对世界模型的不熟稔, 造成语言无法使用. 比如, 对MVC的不熟悉造成Struts难学难用. 对IR不熟悉造成 Lucene 难学难用. 2. 太复杂的抽象造成语言难以入门, 比如 Java 模型造成一行Hello, world 都要至少构造一个 class, 一个 main 和一个 system.out (在Java 6.0中已经无须这么复杂). 一个简单的逻辑映射出不属于这个世界的很多抽象. 不必要的复杂性吓退了初学者. 3. 太高层的抽象造成语言难以理解, 比如 LISP, 抽象于 lambda 演算, 至今仍有不少人对于 Y-Combinator 的高层抽象表现出费解; 而对于精通函数式语言的人来说, Google 的 MapReduce 又显得那么自然. 不了解高度抽象的世界模型, 必然是如同看到满屏幕的乱码一样, 无法下手. 4. 太低的抽象造成语言难以使用, 如C, 在没有library 情况下, 链表都要手工构造. 即使想描述一个简单的思维, 都需要映射出很多不必要的底层抽象. 这样让语言的使用者主观上不倾向于用这套语言体系.

[我最近在学习法语, 其名词的性限制了我的表达, 继而限制了沟通. 因为假如我用错了, 别人会不懂. 因此触发了语言和思维的思考. 同时这几天, 我在考虑到底选择什么样的程序设计语言才是好的选择, 因此有抽象层次和语言表达能力的思考. 正巧有一本维特根斯坦的哲学逻辑在手边, 偶翻一下, 触发了思考, 故写下这篇随笔. 在语言选择方面, 我推荐 Python, 我认为它达到了抽象层次和表达能力的契合, 无论你是初学者, 还是高手].

很多人, 如同你我, 其实都有参与公益事业的时间和动机, 但是这样那样的原因阻碍了我们的贡献. 以我个人为例吧. 在上大学的时候, 我第一次做项目赚了点小钱(2K). 因为父母每月生活费给我都很充裕, 所以这个钱我就随便怎么花了. 我花600元升级了笔记本计算机的硬盘, 花300元买了一个刻录机, 预计留下大约500元给当时的女友买件衣服什么的, 剩下的600元, 我当时很想捐给别人, 算是做件有意义的事情. 那是05年上半年, 小百合上并无什么十大需要捐款的帖子, 我就琢磨着捐给希望工程, 而且我想捐给南京郊区的或者扬州郊区的学生, 这样我还可以了解情况, 看看能不能提供其他帮助, 但是当时希望工程的网页做的不好, 我根本不知道怎么进行一对一捐款. 因此, 这个事情就搁下了. 其实也是部分因为我懒, 其实到南京的希望工程办公室走一走, 这个事情就解决了.

我现在正在参与(其实只是挂名, 还没有任何贡献)一个公益项目: 李笑来老师发起的 有奔头计划. 简单的说, 就是向贫困大学生捐献二手计算机, 并且推广开源软件的使用. 其实我原本的角色是负责往 wiki 里放东西. 充实 wiki 系统. 但是本人这个暑假太懒了, 的确是懒, 没其他原因的懒惰, 居然一点事情都没有做. 所以前几天我和笑来老师说, 我得把我的角色写下来, 给自己定任务, 压迫自己做. 因此我写下我的第一条备忘: 每周为有奔头计划写一篇  Linux 使用文档. 并归类到我 Blog 一个专门分类中, 各位读者监督.

去年我在 MITBBS 上看到可以给 OCEF (Overseas China Education Foundation 海外中国教育基金会)捐款, 就划了 100 美元过去. 然后前段时间我收到邮件, 看到孩子歪歪扭扭的签名收到钱的字迹, 觉得自己终于做了点好事了. 上个月我正式注册会员, 以后可以一对一捐献了. 要是有机会的话, 可以给那些孩子写信. 我看了最近的新闻, 觉得我如果写信, 越发觉得不能市恩, 绝对不能写我是你赞助商这种意思的话. 如果有可能, 最好附上贴好邮票的信封, 看那些西部的孩子们的世界在他们笔下是什么样子. 所以, 备忘之二: 每年写两封信 , 平心静气写, 一笔一划写, 寄给西部小学的或者初中的学生, 告诉他们我眼里世界. 如果有可能, 把信件发到 Blog 上, 大家监督.

最近几个朋友和我打算开展一个”十美元研究基金“的项目. 利用我们的空余时间, 指导低年级本科生科研. 我们利用空余时间, 帮助提供一些软件使用, 程序设计, 研究方法等大方向的指导. 希望尽我们的努力, 以及能够负担的时间和金钱的付出, 帮助更多的本科生, 更早的获得更好的科研训练. 所以, 备忘之三, 每年每季度 帮助一个 对计算机方向科研感兴趣的低年级本科生, 引导他使用开源的软件, 利用互联网等工具, 做一些轻量级的有趣的研究课题.

我不指望我现在做的就能改变世界, 只能说先强迫自己积极参与. 有很多热心人很积极参与公益事业, 用不着所谓的强迫. 我是不那么高尚的人, 用 Blog 备忘和大家监督提高自己吧. 从现在起, 认真做好分配给自己的公益的事情, 或者主动分配给自己一点公益的事情做做, 就是我理解的参与了.

PS: 为了思考我们到底用什么样的态度去参与, 怎么参与, 不断在许下承诺和怀疑能否做到之间挣扎, 这篇文章居然酝酿了我一个星期! 不过既然写下来, 一定要做到!