2010年10月29日星期五

阮一峰的网络日志

阮一峰的网络日志


迷失·上海(组图)

Posted: 28 Oct 2010 09:14 AM PDT

下面是摄影家池磊(《招待所的那些事儿》的作者)的新作品,展出于世博会上海联合企业馆。

转贴自他的网易博客

"这是一组和世博会有关的图片,影调看起来如此的破旧,与现在主流的声音似乎背道而驰,而我认为这看似破旧的画面里其实融合并存在了时下中国城市的种种元素,他就像一个巨大的混音器把和谐之外的各种信号揉搓在了一起,我相信这是这个时代真实的写照!最起码是一种不一样的声音!

顺便说一下,很多人问我,你的照片为什么总是有后期合成的成分,为什么你不直接用胶片拍摄纪实的东西?关于这个问题我在这里做一个回答,胶片和数字艺术是两个完全不同领域,我的照片需要前期构思融合的因素很多!它不仅仅是简单的记录,意义已经超越了摄影本身,我认为这是一种更有难度的图片艺术形式!这需要缜密的思考和更多的工作,我的每一组作品就是一幅巨大的中国现在浮世绘,这张浮世绘折射了梦幻般真实的中国社会。"

我喜欢这组作品,佩服作者的创造能力。它们确实"梦幻般真实"地再现了这个光怪陆离的城市。

==============================================

(完)


2010年10月27日星期三

阮一峰的网络日志

阮一峰的网络日志


什么是主权?

Posted: 26 Oct 2010 05:42 AM PDT

昨天,读到一篇好文章。

请允许我不提作者和篇名,只引用其中一段关于主权的话。

"最早的时候,主权的概念常常是专制统治的象征。

但是,美国的《独立宣言》与法国的《人权宣言》改写了这一定义。公民的权利成为国家权力的基础。

二战后,主权的概念再一次改变。国家主义向国际主义转变。联合国的《世界人权宣言》认为,'主权国家'不再拥有至高无上的、不受限制的权力。"

我恍然大悟,天天都听到"主权不容侵犯,内政不容干涉"这句话,可是我从来没想过什么是主权。这里面确实很可疑。凭什么有了主权,你就变得"刀枪不入",可以自行其是呢?

======================================

根据上面这段话,主权的概念也是发展的,可以分成三个阶段。

第一阶段,主权就是国王的统治权。

我是国王,我爱干什么就干什么,你管得着嘛!不允许你侵犯我的主权。

第二阶段,主权就是人民的统治权,不允许国王独享。

1776年,美国《独立宣言》写得很清楚,当人民与国王发生冲突时,人民有权推翻国王,也就是说"主权在民"。

"国王的权力,来自于被统治者的同意。当国王破坏人民的权利时,人民有权改变或废除国王。"

1793年,法国大革命的《人权宣言》写得更直接,绝不接受国王对人民的压迫。

"当国王违犯人民的权利时,对于人民及一部分人民而论,起义就是最神圣的权利和最不可缺少的义务。"

第三阶段,主权就是不违反普世价值的统治权,不存在不受任何"侵犯"的主权。

1948年,这一点写进了联合国的《世界人权宣言》。

"人人有资格享有本宣言所载的一切权利和自由,不因国家不同而有所区别。"

"任何国家不得破坏本宣言所载的任何权利和自由。"

"人人有权要求本宣言所载的权利和自由获得充分实现。"

最后一句话中的"人人",显然包括外国人。那么,当A国青年走上A国的街头,抗议B国不公平不正义时,B国外交部是否可以说,本国的"主权不容侵犯,内政不容干涉"呢?

(完)


2010年10月25日星期一

阮一峰的网络日志

阮一峰的网络日志


从0到1亿美元 ---- PopCap创始人John Vechey自述

Posted: 24 Oct 2010 07:43 AM PDT

著名游戏"植物大战僵尸",是PopCap软件公司的作品。

这家公司今年的收入,有望达到1亿美元。可是10年前,它刚刚成立的时候,只是一个辍学生与两个好友开的软件工作室。

它如何做到从0到1亿美元的飞跃?下面就是创始人非常诚恳的自述,我看了以后实在难忘,就把它译了出来。

========================================

PopCap创始人John Vechey自述

译者:阮一峰

原文网址:inc.com


1.

我在威斯康辛州长大。我爸是炼钢工人,他深受嬉皮士运动的影响,把朋友、家庭、女人看得比钱重要,所以他从不接受全职工作,只在需要钱的时候才干一些兼职。

2.

我很小的时候,父母就离婚了。我跟着我妈,后来一共有7个继父。那时家里非常穷,我看见别人有好吃的就很羡慕。课余时间我都在打工,所以我的青少年时代几乎没有课外活动。

3.

1996年,我进了普渡大学。虽然我还没有自己的电脑,但我想成为程序员,所以就选修了编程课。那门课所有考试的第一名,总是一个叫做Brian Fiete的人,我就去问他,愿不愿意一起编个游戏。这就是我们的第一个游戏ARC的由来。

4.

ARC是一个彩弹游戏,放上网以后,吸引了许多人玩。开发游戏需要大量时间,而我的大学GPA(绩点)只有1.67,所以我就退学了,全身心投入开发工作。有一次网上聊天,一个叫做Jason Kapalka的人要求加入我们,他后来成了PopCap的第三个创始人。那是1997年的事。

5.

Jason Kapalka说服一家游戏公司,向我们购买ARC运营许可证,价格是45000美元。那一年,我只有19岁,突然有这么一笔钱,感觉像百万富翁一样。后来,我们又把ARC的所有权卖给了Sierra游戏公司,又拿到了10万美元,我们三个就开始成立自己的公司了。

6.

最初,我们只打算为别人开发游戏,没想过直接以自己的品牌向玩家推出游戏。我们第一个开发出来的游戏,被代理商拒绝了。然后,我们就做了Bejeweled(钻石迷情)。那个游戏的诞生纯属偶然,我回老家的时候,看见人们在网上玩一种纸牌游戏,觉得很有意思,就发Email告诉Brian Fiete,他第二天就写出了游戏的原型,而Jason Kapalka在第三天找来了许多钻石图案,第四天Bejeweled的第一版就成型了。

7.

我们带着Bejeweled去找游戏网站,Yahoo表示没兴趣代理,Microsoft表示可以把它放到MSN上,但是条件很苛刻,出于无奈,我们还是接受了。结果,它在MSN上大受欢迎,一天有6万个人来玩,可是我们的报酬仅仅是每月1500美元。

8.

到了2000年,用户要求获得Bejeweled的可下载版本,因为那时大家都是拨号上网,网费很贵,而且上网时电话打不进来。于是,我不得不说服Yahoo(它也开始代理Bejeweled了)和MSN,在继续提供免费的线上版本的同时,允许我们推出售价20美元的功能更强的可下载版本,所获得的利润,我们与销售网站对半分。我们从此有了一种新的商业模式。

9.

2001年,Bejeweled上线销售的第一个月,我们赚到了35000美元,第二个月40000美元。我们知道,这种状况不会持续很久,不过这些钱已经足够了。我和Brian拿着钱去阿根廷度假了,我们在那里天天喝酒,一连过了4个月。直到Yahoo聘请我们开发新游戏时,才重新回到美国。

10.

我们决定,我们开发的游戏一定要非常有趣、容易上手、能够吸引全家老小。果然,此后开发的35款游戏都赚到了钱。2004年,我们有15个雇员,有人愿意出6000万美元收购我们,我们拒绝了。我们找到David Roberts担任CEO,他曾经为Apple和 Adobe工作。我们告诉他,他的任务就是做大销售额,游戏创意方面的事情,他不用管。

11.

David Roberts上任以后,改变的第一件事,就是让同一款游戏可以长期产生收入,而不是像我们以前那样,通过不断开发新游戏获得收入。如今,单单Bejeweled的收入就占到总收入的30%以上,因为我们让它可以在所有平台上运行:PC/Mac、 Xbox、PlayStation、Wii、DS、PalmPilot、iPhone、iPad等等。

12.

2006年开始,我们用了三年时间,不断完善《植物大战僵尸》这款新游戏,最后获得了巨大成功。我们始终有一个信念,那就是一定要做出顶尖游戏,那样才能赚到大钱,如果一个游戏只是勉强可玩,那就一分钱也赚不到。

(完)


2010年10月22日星期五

阮一峰的网络日志

阮一峰的网络日志


什么是边际?

Posted: 22 Oct 2010 05:07 AM PDT

这几天睡觉前,我都在读《顾准的最后25年》。

这本书的优点是对历史的记录比较准确和客观,缺点是不涉及深层次的问题,分析得比较浅,但是还算值得一读。(下载地址

==============================

书中写了一件事,很有意义,值得分享。

话说1959年,为了保证粮食产量,根据毛泽东主席的指示,中共中央发出了《关于农业的五条紧急指示》。其中的第二条指示是这样的:

"二、......做好麦收的一切准备,保证丰产丰收,颗粒还家。"

于是,各地根据指示的精神,发起了"颗粒还家"运动。也就是说,要求把掉落在地里的麦穗都捡回家。因为中央的要求是"颗粒还家",所以理论上,必须把每一颗麦粒都捡回家。

此时,顾准正在河南商城县的干校劳动改造。他听到"颗粒还家"这个口号以后,私下对同伴说,中央的这个指示是错的,因为它在经济上不合算。

他说:

"如果一个农家老妇上地里捡麦穗,每天能捡回十斤八斤麦子当然很好,但是后来如果每天只能捡回一斤麦子,而她每天的消耗也要一斤麦子时,就到了边界(,就不应该再去捡了)。"

顾准的意思是说,地里的麦穗总是越捡越少,每一天的成果都是递减的。那么总有一天,捡到的麦穗还不够弥补付出的劳动力,这时就不应该再捡了,剩下的"颗粒"不必再"还家"了,否则在经济上就是蠢事了。

我看到这段话,马上意识到,顾准讲到的就是经济学的"边际"概念。他的这个例子如此通俗,是我见过的最简单易懂的对"边际"的解释。

"边际"(margin)是经济学的基础概念,相当抽象。一般的教科书上总是解释,所谓"边际"就是下一个单位的量,在数学上就是某一个点对应的导数。这样的描述,初学者不容易理解,现在有了顾准这个例子,一切就简单多了。

什么是边际?它就是你在下一天捡到的麦穗数量。

"边际产量递减"是经济学的基本原理,即你每天捡到的麦穗必定越来越少。推论就是,边际产量等于边际投入时,利润最大,即当你捡到的麦穗等于你付出的成本的那一天,整个"颗粒还家"行动的净效益达到最大。

========================

顾准能够如此熟练地运用经济学原理,分析现实问题,真是很有水平。可惜中央的政策岂能随便被质疑,一个小人物自以为是地评点权威,不是自寻死路吗?像他这种独立思考、又忍不住要说出来的人,难怪一生会如此坎坷凄凉。

(完)


2010年10月17日星期日

阮一峰的网络日志

阮一峰的网络日志


朱学恒 vs 卢映洁

Posted: 17 Oct 2010 05:11 AM PDT

一、

今天,台湾的朱学恒在blog上透露,他收到了一封律师函。

中正大学法律系的卢映洁副教授,要求朱学恒公开道歉,否则就要起诉他。原因是卢映洁主张废除死刑,而朱学恒强烈反对,并且"侮辱、诽谤"了卢映洁。

二、

律师函是这样写的:

【受文者】朱学恒先生

【速别】最速件

【发文日期】中华民国99年10月4日

【发文字号】第991004002号

【附件】道歉暨具结书一份


【主旨】

就台端于TVBS电视台5月15日之《2100周末开讲》节目所发表之不当言论、涉及不法等情事,依法提出严正声明及异议,并催告台端应于文到两个星期内,履行向本人道歉及捐款予慈善团体等要求,内容详如说明栏所载,敬请予以查照,并依限办理为荷。


【说明】

一,本人曾于今年(99年)4月间因参与电视节目,发表支持废除死刑言论,而遭网友侮辱谩骂,恐吓之言词几近人身攻击,因而在5月初具状向嘉义县民雄分局对这些网路言论提出控告,并请求追查发文人士之身份。本人状告网友之消息在5月12日之后于某些平面媒体与电视媒体曝光后,朱学恒先生即开始进行封本人之诽谤行为,分述如下:

(一)今年3月间,网民潘宜宗在facebook对本人的侮辱诽谤言论:"这种货色竟然还是中正大学法学律系副教授,看来中正大学的法律系学生前途堪忧喔",被本人控告的消息见报后,朱学恒于5月12日在其"阿宅万事通事务所"的facebook版,称赞网友潘宜宗为烈士,而且还称"其他受到压迫者同样欢迎出面"。实则,《联合报》在5月12日有关本人状告网友的新闻报导,其中清楚提到网友是以不堪的言语对本人加以侮辱,例如"脑袋破洞"、"(得了)迷恋罪犯症"、"希望她被强奸一百遍"等等。本人面对这些侮辱谩骂、几近恐吓之言词,依法提出控告。朱学恒竟然颠倒是非,不但对这些发表侮辱谩骂言论的人加以鼓励,还以恶意抹黑、诽谤方式,指称本人为压迫者,刻意引导网民更加肆无忌惮地在朱学恒的facebook版面上谩骂,例如5月14日网民傅化谊"她提告只有自爽+丢脸的作用,真不亏为"国立"大学法律系的叫兽"、陈彦铭"那我今天跑去扇这位脑袋有洞的叫兽耳光......发起扇叫兽耳光运动......"等不堪言论。对此朱学恒亦刻意不予删除,其恶意妨害名誉行为十分明显。

(二)朱学恒于5月15日之《2100周末开讲》节目中,出示本人先前在网路上给予网友的一份公开说明,表示:"......再看最后一段"我会想办法去向你们的父母或亲友或上司了解一下你们这些人的人格与行为举止,并想一下如何让你们理解言论自由的界限,及预防你们再犯的方法"。这听起来很恐怖,对不对?我今天不但要告你,我还要知道你的爸爸妈妈是谁、你在哪里工作,然后我要让你了解到底是怎么一回事......"。对于本人身为教育工作者希望了解网友做出侮辱攻击言论之原因,以及希望网友可以有好的外在监督者进行有效监督之用意,朱学恒先生却恶意抹黑,描述为恐吓网友,刻意妖魔化本人。朱学恒先生亦已为人父,却不知正确的价值观与良好身教之重要性,若再任凭朱学恒先生如此误导,日后其子长大,在学校或社会上犯错,若师长欲找家长或其他监督者导正其行为,朱学恒先生之子可能会大声指责师长为恐怖分子,是在恐吓他,而永远不愿反省自己的过错,终究害己。

(三)朱学恒一直任由那些不知言论自由界限的网民在其Facebook上一再对本人进行人身攻击,并加以鼓励。例如朱学恒明知潘宜宗在Facebook对本人的侮辱诽谤言论是"这种'货色'竟然还是中正大学法律系副教授,看来中正大学的法律系学生前途堪忧喔",而不是"这样的人可以当教授吗?",却在5月15日的节目恶意隐藏事实,提供潘宜宗电话供主持人现场采访(称对方为小胖),而潘宜宗在电视节目上刻意称只有说:"这样的人可以当教授吗?",之后潘宜宗又在朱学恒的facebook上发文沾沾自喜。此外,潘宜宗更在其他网站(诸如Mobile 01)继续其侮辱诽谤言论,亦在朱学恒的facebook上号召网友打电话打爆中正大学法学院,并在其中谎称法学院秘书对网友爆料本人之八卦。朱学恒刻意不删除此等不实言论,其希望本人名誉受损之心态不言而喻。

二,基于上述,朱学恒先生于网路上与电视节目中恶意诽谤行为至为明显,涉及刑事责任与民事责任;又对于自己网站上他人侮辱诽谤言论不加删除,应负共同侵权之民事责任。上述事实皆证据确凿。是故,促朱学恒先生收到本律师函两个星期内,履行向本人道歉及捐款慈善团体之要求(见附件:道歉暨具结书),并将道歉暨具结书以及捐款凭据寄回嘉义县民雄乡三兴村7邻大学路一段168号,否则本人将于法定期限内追究朱学恒先生之民事及刑事责任。

【正本收受者】

朱学恒
(10462台北市中山区通北街91号6楼/财团法人奇幻文化艺术基金会)
(23574台北县中和市中和路306号八楼之一/财团法人奇幻文化艺术基金会)

【副本收受者】

卢映洁

三、

上面的律师函其实是有点黑色幽默的,一点都不专业,根本不像出自法律系副教授之手。更可笑的是,居然还有一个附件,要求朱学恒签名。

道歉暨具结书

1. 本人朱学恒于2010年5月间在"阿宅万事通事务所"的facebook版上以及2010年5月15日之TVBS电视台的《2100周末开讲节目》中,对卢映洁教授发表诽谤言论,就此向卢映洁教授表达道歉之意。

2.本人朱学恒依卢映洁教授之要求,捐款新台币壹拾伍万元至医疗财团法人"正德癌症医疗基金会"。

3.本人朱学恒依卢映洁教授之要求,捐款新台币伍万元至财团法人"民间司法改革基金会"。

4.本人朱学恒承诺,日后凡是与有朱学恒名义有关之网路、电视广播节目或其他活动,其中若有出现死刑议题、犯罪被害人保护议题以及法律或司法改革议题之文字或言论,本人朱学恒有义务维持理性公平的讨论,删除或取消他人侮辱、人身攻击的文字或言论,并且取得财团法人民间司法改革基金会之代表人士的意见,在该相关网路、电视广播节目或其他活动中,以适当篇幅加以呈现。若有违反,本人朱学恒每次愿支付新台币壹万元至医疗财团法人"正德癌症医疗基金会"与财团法人"民间司法改革委员会"。

立道歉暨具结书人 朱学恒

亲自签名:

中华民国 年 月 日

四、

这封道歉信中,最重要的是第四条。朱学恒要是签了,不仅脸面丢尽,而且以后几乎就不能公开发言了。请仔细看看,第四条是这样的:

"本人朱学恒承诺,日后凡是与有朱学恒名义有关之网路、电视广播节目或其他活动,其中若有出现死刑议题、犯罪被害人保护议题以及法律或司法改革议题之文字或言论,本人朱学恒有义务维持理性公平的讨论,删除或取消他人侮辱、人身攻击的文字或言论,并且取得财团法人民间司法改革基金会之代表人士的意见,在该相关网路、电视广播节目或其他活动中,以适当篇幅加以呈现。若有违反,本人朱学恒每次愿支付新台币壹万元至医疗财团法人"正德癌症医疗基金会"与财团法人"民间司法改革委员会"。"

这明摆着就是不让你说话了,看来这位副教授要求道歉是假,对朱学恒进行侮辱和挑衅是真。

下面就是这件事有意思的部分了。因为她找错了人,朱学恒可是不好惹的。

五、

首先,朱学恒对这件事进行评论:

"她的用意根本不是捍卫名誉权,而是压制不同的言论!

如果一个国立大学的法律系教授,这样去对待学生,我们将会面对的是什么样的社会?

想想看!

这种用法律手段打压对立言论的恶例绝不能开!

如果没有别人愿意捍卫言论自由,我愿意抵挡这样的恶劣手段。

我们必需让社会大众知道,动辄用法律手段压制反对言论,是绝对不正确,是绝对不应该,也绝对不可以成功的!

如果没有别人愿意当那个盾牌,我愿意站上法庭当那个盾牌。

如果没有别人愿意当那个防线,我愿意站上第一线当那个防线。

这封函件的任何要求,我都不会同意,也不会执行。

不要拖延,赶快来告,我都准备好了!

这就是我送给我儿子朱小胖最好的一岁生日礼物,儿子啊,一岁生日快乐。你爸爸可能会被告,可能会被关,但是你爸爸是不会屈服的。

看着你爸爸的背影,试着学到一些东西吧!"

六、

然后,他做出了公开答复:

接着,就是这件事最高潮的部分,他把这个答复做成巨幅海报,让货运公司运到中正大学,挂在卢映洁副教授办公室对面的楼上!

七、

朱学恒的执行力和创造力,太令我佩服了!而且,我还想说,这样的答复真是解气!

网络的言论自由,其实是最不值钱的自由。如果还有其他渠道可以声张权利,谁会选择网络啊,说了都不知道被谁听到!只有那些什么权利也没有的弱势群体,才会被迫在网络上表达心声。

可是,有人就是连这点权利都要剥夺,挥舞法律的狼牙棒,进行恐吓,企图把弱势者最后一点反抗都消灭干净。这样的人活该被骂,而且注定失败。

(完)


2010年10月16日星期六

阮一峰的网络日志

阮一峰的网络日志


排版六原则

Posted: 16 Oct 2010 01:44 AM PDT

上个月,我贴了《图形化简历》

几天后,就收到了秋叶老师的来信,希望与我探讨一些设计问题。他写过一本畅销书《说服力-让你的PPT会说话》,眼下正在写续集。

我看了新书的样章,觉得很不错,有些内容很值得分享。

====================================

首先,我们先看一个例子。良好的设计如何使得一个平庸的文档脱胎换骨。下面是一张大学生的求职简历,再普通不过了,想要引起招聘经理的注意,恐怕很难。

秋叶老师对它进行了简单的排版,还是一张表格,还是黑白配色,没有使用任何图形元素,效果却完全不一样了。

真是令人眼前一亮,不由自主地想多看几眼。这就是优秀设计的作用:它让你脱颖而出。

====================================

秋叶老师把他的排版心得,总结为六个原则:对齐,聚拢,重复,对比,强调,留白。我是这样理解的:

一、对齐原则

  相关内容必须对齐,次级标题必须缩进,方便读者视线快速移动,一眼看到最重要的信息。

二、聚拢原则

  将内容分成几个区域,相关内容都聚在一个区域中。段间距应该大于段内的行距。

三、留白原则

  千万不要把页面排得密密麻麻,要留出一定的空白,这本身就是对页面的分隔。这样既减少了页面的压迫感,又可以引导读者视线,突出重点内容。

四、降噪原则

  颜色过多、字数过多、图形过繁,都是分散读者注意力的"噪音"。

五、重复原则

  重要信息值得重复出现。

六、对比原则

  加大不同元素的视觉差异。这样既增加了页面的活泼,又方便读者集中注意力阅读某一个子区域。

====================================

下面用一个PPT的例子,演示排版六原则。

上面这张ppt有两个毛病。一是字数太多,抓不住重点;二是右边没有对齐,使得读者的视线只能一行行地从行首到行尾移动,不能直上直下。

现在进行修改。

第一步,根据"聚拢原则",将六点分成六个区域。

第二步,根据"降噪原则",将每一点分成"小标题"和"说明文字"两部分。

第三步,根据"对齐原则",将每一个部分、每一种元素对齐。

第四步,根据"对比原则",加大"小标题"和"说明文字"在字体和颜色上的差异。

第五步,根据"留白原则",留出一定的空白。

页面的可读性大大增加。

(完)


2010年10月14日星期四

阮一峰的网络日志

阮一峰的网络日志


为什么Lisp语言如此先进?(译文)

Posted: 13 Oct 2010 08:40 PM PDT

上周,《黑客与画家》总算翻译完成,已经交给出版社了。

翻译完这本书,累得像生了一场大病。把书稿交出去的时候,心里空荡荡的,也不知道自己得到了什么,失去了什么。

希望这个中译本和我的努力,能得到读者认同和肯定。

下面是此书中非常棒的一篇文章,原文写于八年前,至今仍然具有启发性,作者眼光之超前令人佩服。由于我不懂Lisp语言,所以田春同学帮忙校读了一遍,纠正了一些翻译不当之处,在此表示衷心感谢。

============================

为什么Lisp语言如此先进?

作者:Paul Graham

译者:阮一峰

英文原文:Revenge of the Nerds

(节选自即将出版的《黑客与画家》中译本)


一、

如果我们把流行的编程语言,以这样的顺序排列:Java、Perl、Python、Ruby。你会发现,排在越后面的语言,越像Lisp。

Python模仿Lisp,甚至把许多Lisp黑客认为属于设计错误的功能,也一起模仿了。至于Ruby,如果回到1975年,你声称它是一种Lisp方言,没有人会反对。

编程语言现在的发展,不过刚刚赶上1958年Lisp语言的水平。

二、

1958年,John McCarthy设计了Lisp语言。我认为,当前最新潮的编程语言,只是实现了他在1958年的设想而已。

这怎么可能呢?计算机技术的发展,不是日新月异吗?1958年的技术,怎么可能超过今天的水平呢?

让我告诉你原因。

这是因为John McCarthy本来没打算把Lisp设计成编程语言,至少不是我们现在意义上的编程语言。他的原意只是想做一种理论演算,用更简洁的方式定义图灵机。

所以,为什么上个世纪50年代的编程语言,到现在还没有过时?简单说,因为这种语言本质上不是一种技术,而是数学。数学是不会过时的。你不应该把Lisp语言与50年代的硬件联系在一起,而是应该把它与快速排序(Quicksort)算法进行类比。这种算法是1960年提出的,至今仍然是最快的通用排序方法。

三、

Fortran语言也是上个世纪50年代出现的,并且一直使用至今。它代表了语言设计的一种完全不同的方向。Lisp是无意中从纯理论发展为编程语言,而Fortran从一开始就是作为编程语言设计出来的。但是,今天我们把Lisp看成高级语言,而把Fortran看成一种相当低层次的语言。

1956年,Fortran刚诞生的时候,叫做Fortran I,与今天的Fortran语言差别极大。Fortran I实际上是汇编语言加上数学,在某些方面,还不如今天的汇编语言强大。比如,它不支持子程序,只有分支跳转结构(branch)。

Lisp和Fortran代表了编程语言发展的两大方向。前者的基础是数学,后者的基础是硬件架构。从那时起,这两大方向一直在互相靠拢。Lisp刚设计出来的时候,就很强大,接下来的二十年,它提高了自己的运行速度。而那些所谓的主流语言,把更快的运行速度作为设计的出发点,然后再用超过四十年的时间,一步步变得更强大。

直到今天,最高级的主流语言,也只是刚刚接近Lisp的水平。虽然已经很接近了,但还是没有Lisp那样强大。

四、

Lisp语言诞生的时候,就包含了9种新思想。其中一些我们今天已经习以为常,另一些则刚刚在其他高级语言中出现,至今还有2种是Lisp独有的。按照被大众接受的程度,这9种思想依次是:

  1. 条件结构(即"if-then-else"结构)。现在大家都觉得这是理所当然的,但是Fortran I就没有这个结构,它只有基于底层机器指令的goto结构。

  2. 函数也是一种数据类型。在Lisp语言中,函数与整数或字符串一样,也属于数据类型的一种。它有自己的字面表示形式(literal representation),能够储存在变量中,也能当作参数传递。一种数据类型应该有的功能,它都有。

  3. 递归。Lisp是第一种支持递归函数的高级语言。

  4. 变量的动态类型。在Lisp语言中,所有变量实际上都是指针,所指向的值有类型之分,而变量本身没有。复制变量就相当于复制指针,而不是复制它们指向的数据。

  5. 垃圾回收机制。

  6. 程序由表达式(expression)组成。Lisp程序是一些表达式区块的集合,每个表达式都返回一个值。这与Fortran和大多数后来的语言都截然不同,它们的程序由表达式和语句(statement)组成。

区分表达式和语句,在Fortran I中是很自然的,因为它不支持语句嵌套。所以,如果你需要用数学式子计算一个值,那就只有用表达式返回这个值,没有其他语法结构可用,因为否则就无法处理这个值。

后来,新的编程语言支持区块结构(block),这种限制当然也就不存在了。但是为时已晚,表达式和语句的区分已经根深蒂固。它从Fortran扩散到Algol语言,接着又扩散到它们两者的后继语言。

  7. 符号(symbol)类型。符号实际上是一种指针,指向储存在哈希表中的字符串。所以,比较两个符号是否相等,只要看它们的指针是否一样就行了,不用逐个字符地比较。

  8. 代码使用符号和常量组成的树形表示法(notation)。

  9. 无论什么时候,整个语言都是可用的。Lisp并不真正区分读取期、编译期和运行期。你可以在读取期编译或运行代码;也可以在编译期读取或运行代码;还可以在运行期读取或者编译代码。

在读取期运行代码,使得用户可以重新调整(reprogram)Lisp的语法;在编译期运行代码,则是Lisp宏的工作基础;在运行期编译代码,使得Lisp可以在Emacs这样的程序中,充当扩展语言(extension language);在运行期读取代码,使得程序之间可以用S-表达式(S-expression)通信,近来XML格式的出现使得这个概念被重新"发明"出来了。

五、

Lisp语言刚出现的时候,它的思想与其他编程语言大相径庭。后者的设计思想主要由50年代后期的硬件决定。随着时间流逝,流行的编程语言不断更新换代,语言设计思想逐渐向Lisp靠拢。

思想1到思想5已经被广泛接受,思想6开始在主流编程语言中出现,思想7在Python语言中有所实现,不过似乎没有专用的语法。

思想8可能是最有意思的一点。它与思想9只是由于偶然原因,才成为Lisp语言的一部分,因为它们不属于John McCarthy的原始构想,是由他的学生Steve Russell自行添加的。它们从此使得Lisp看上去很古怪,但也成为了这种语言最独一无二的特点。Lisp古怪的形式,倒不是因为它的语法很古怪,而是因为它根本没有语法,程序直接以解析树(parse tree)的形式表达出来。在其他语言中,这种形式只是经过解析在后台产生,但是Lisp直接采用它作为表达形式。它由列表构成,而列表则是Lisp的基本数据结构。

用一门语言自己的数据结构来表达该语言,这被证明是非常强大的功能。思想8和思想9,意味着你可以写出一种能够自己编程的程序。这可能听起来很怪异,但是对于Lisp语言却是再普通不过。最常用的做法就是使用宏。

术语"宏"在Lisp语言中,与其他语言中的意思不一样。Lisp宏无所不包,它既可能是某样表达式的缩略形式,也可能是一种新语言的编译器。如果你想真正地理解Lisp语言,或者想拓宽你的编程视野,那么你必须学习宏。

就我所知,宏(采用Lisp语言的定义)目前仍然是Lisp独有的。一个原因是为了使用宏,你大概不得不让你的语言看上去像Lisp一样古怪。另一个可能的原因是,如果你想为自己的语言添上这种终极武器,你从此就不能声称自己发明了新语言,只能说发明了一种Lisp的新方言。

我把这件事当作笑话说出来,但是事实就是如此。如果你创造了一种新语言,其中有car、cdr、cons、quote、cond、atom、eq这样的功能,还有一种把函数写成列表的表示方法,那么在它们的基础上,你完全可以推导出Lisp语言的所有其他部分。事实上,Lisp语言就是这样定义的,John McCarthy把语言设计成这个样子,就是为了让这种推导成为可能。

六、

就算Lisp确实代表了目前主流编程语言不断靠近的一个方向,这是否意味着你就应该用它编程呢?

如果使用一种不那么强大的语言,你又会有多少损失呢?有时不采用最尖端的技术,不也是一种明智的选择吗?这么多人使用主流编程语言,这本身不也说明那些语言有可取之处吗?

另一方面,选择哪一种编程语言,许多项目是无所谓的,反正不同的语言都能完成工作。一般来说,条件越苛刻的项目,强大的编程语言就越能发挥作用。但是,无数的项目根本没有苛刻条件的限制。大多数的编程任务,可能只要写一些很小的程序,然后用胶水语言把这些小程序连起来就行了。你可以用自己熟悉的编程语言,或者用对于特定项目来说有着最强大函数库的语言,来写这些小程序。如果你只是需要在Windows应用程序之间传递数据,使用Visual Basic照样能达到目的。

那么,Lisp的编程优势体现在哪里呢?

七、

语言的编程能力越强大,写出来的程序就越短(当然不是指字符数量,而是指独立的语法单位)。

代码的数量很重要,因为开发一个程序耗费的时间,主要取决于程序的长度。如果同一个软件,一种语言写出来的代码比另一种语言长三倍,这意味着你开发它耗费的时间也会多三倍。而且即使你多雇佣人手,也无助于减少开发时间,因为当团队规模超过某个门槛时,再增加人手只会带来净损失。Fred Brooks在他的名著《人月神话》(The Mythical Man-Month)中,描述了这种现象,我的所见所闻印证了他的说法。

如果使用Lisp语言,能让程序变得多短?以Lisp和C的比较为例,我听到的大多数说法是C代码的长度是Lisp的7倍到10倍。但是最近,New Architect杂志上有一篇介绍ITA软件公司的文章,里面说"一行Lisp代码相当于20行C代码",因为此文都是引用ITA总裁的话,所以我想这个数字来自ITA的编程实践。 如果真是这样,那么我们可以相信这句话。ITA的软件,不仅使用Lisp语言,还同时大量使用C和C++,所以这是他们的经验谈。

根据上面的这个数字,如果你与ITA竞争,而且你使用C语言开发软件,那么ITA的开发速度将比你快20倍。如果你需要一年时间实现某个功能,它只需要不到三星期。反过来说,如果某个新功能,它开发了三个月,那么你需要五年才能做出来。

你知道吗?上面的对比,还只是考虑到最好的情况。当我们只比较代码数量的时候,言下之意就是假设使用功能较弱的语言,也能开发出同样的软件。但是事实上,程序员使用某种语言能做到的事情,是有极限的。如果你想用一种低层次的语言,解决一个很难的问题,那么你将会面临各种情况极其复杂、乃至想不清楚的窘境。

所以,当我说假定你与ITA竞争,你用五年时间做出的东西,ITA在Lisp语言的帮助下只用三个月就完成了,我指的五年还是一切顺利、没有犯错误、也没有遇到太大麻烦的五年。事实上,按照大多数公司的实际情况,计划中五年完成的项目,很可能永远都不会完成。

我承认,上面的例子太极端。ITA似乎有一批非常聪明的黑客,而C语言又是一种很低层次的语言。但是,在一个高度竞争的市场中,即使开发速度只相差两三倍,也足以使得你永远处在落后的位置。

附录:编程能力

为了解释我所说的语言编程能力不一样,请考虑下面的问题。我们需要写一个函数,它能够生成累加器,即这个函数接受一个参数n,然后返回另一个函数,后者接受参数i,然后返回n增加(increment)了i后的值。

Common Lisp的写法如下:

  (defun foo (n)
    (lambda (i) (incf n i)))

Ruby的写法几乎完全相同:

  def foo (n)
    lambda {|i| n += i } end

Perl 5的写法则是:

  sub foo {
    my ($n) = @_;
    sub {$n += shift}
  }

这比Lisp和Ruby的版本,有更多的语法元素,因为在Perl语言中,你不得不手工提取参数。

Smalltalk的写法稍微比Lisp和Ruby的长一点:

  foo: n
    |s|
    s := n.
    ^[:i| s := s+i. ]

因为在Smalltalk中,局部变量(lexical variable)是有效的,但是你无法给一个参数赋值,因此不得不设置了一个新变量,接受累加后的值。

Javascript的写法也比Lisp和Ruby稍微长一点,因为Javascript依然区分语句和表达式,所以你需要明确指定return语句,来返回一个值:

  function foo (n) {
    return function (i) {
      return n += i } }

(实事求是地说,Perl也保留了语句和表达式的区别,但是使用了典型的Perl方式处理,使你可以省略return。)

如果想把Lisp/Ruby/Perl/Smalltalk/Javascript的版本改成Python,你会遇到一些限制。因为Python并不完全支持局部变量,你不得不创造一种数据结构,来接受n的值。而且尽管Python确实支持函数数据类型,但是没有一种字面量的表示方式(literal representation)可以生成函数(除非函数体只有一个表达式),所以你需要创造一个命名函数,把它返回。最后的写法如下:

  def foo (n):
    s = [n]
    def bar (i):
      s[0] += i
      return s[0]
    return bar

Python用户完全可以合理地质疑,为什么不能写成下面这样:

  def foo (n):
    return lambda i: return n += i

或者:

  def foo (n):
    lambda i: n += i

我猜想,Python有一天会支持这样的写法。(如果你不想等到Python慢慢进化到更像Lisp,你总是可以直接......)

在面向对象编程的语言中,你能够在有限程度上模拟一个闭包(即一个函数,通过它可以引用由包含这个函数的代码所定义的变量)。你定义一个类(class),里面有一个方法和一个属性,用于替换封闭作用域(enclosing scope)中的所有变量。这有点类似于让程序员自己做代码分析,本来这应该是由支持局部作用域的编译器完成的。如果有多个函数,同时指向相同的变量,那么这种方法就会失效,但是在这个简单的例子中,它已经足够了。

Python高手看来也同意,这是解决这个问题的比较好的方法,写法如下:

  def foo (n):
    class acc:
      def _ _init_ _ (self, s):
        self.s = s
      def inc (self, i):
        self.s += i
        return self.s
    return acc (n).inc

或者

  class foo:
    def _ _init_ _ (self, n):
      self.n = n
    def _ _call_ _ (self, i):
      self.n += i
      return self.n

我添加这一段,原因是想避免Python爱好者说我误解这种语言。但是,在我看来,这两种写法好像都比第一个版本更复杂。你实际上就是在做同样的事,只不过划出了一个独立的区域,保存累加器函数,区别只是保存在对象的一个属性中,而不是保存在列表(list)的头(head)中。使用这些特殊的内部属性名(尤其是__call__),看上去并不像常规的解法,更像是一种破解。

在Perl和Python的较量中,Python黑客的观点似乎是认为Python比Perl更优雅,但是这个例子表明,最终来说,编程能力决定了优雅。Perl的写法更简单(包含更少的语法元素),尽管它的语法有一点丑陋。

其他语言怎么样?前文曾经提到过Fortran、C、C++、Java和Visual Basic,看上去使用它们,根本无法解决这个问题。Ken Anderson说,Java只能写出一个近似的解法:

  public interface Inttoint {
    public int call (int i);
  }

  public static Inttoint foo (final int n) {
    return new Inttoint () {
    int s = n;
    public int call (int i) {
    s = s + i;
    return s;
    }};
  }

这种写法不符合题目要求,因为它只对整数有效。

当然,我说使用其他语言无法解决这个问题,这句话并不完全正确。所有这些语言都是图灵等价的,这意味着严格地说,你能使用它们之中的任何一种语言,写出任何一个程序。那么,怎样才能做到这一点呢?就这个小小的例子而言,你可以使用这些不那么强大的语言,写一个Lisp解释器就行了。

这样做听上去好像开玩笑,但是在大型编程项目中,却不同程度地广泛存在。因此,有人把它总结出来,起名为"格林斯潘第十定律"(Greenspun's Tenth Rule):

"任何C或Fortran程序复杂到一定程度之后,都会包含一个临时开发的、只有一半功能的、不完全符合规格的、到处都是bug的、运行速度很慢的Common Lisp实现。"

如果你想解决一个困难的问题,关键不是你使用的语言是否强大,而是好几个因素同时发挥作用(a)使用一种强大的语言,(b)为这个难题写一个事实上的解释器,或者(c)你自己变成这个难题的人肉编译器。在Python的例子中,这样的处理方法已经开始出现了,我们实际上就是自己写代码,模拟出编译器实现局部变量的功能。

这种实践不仅很普遍,而且已经制度化了。举例来说,在面向对象编程的世界中,我们大量听到"模式"(pattern)这个词,我觉得那些"模式"就是现实中的因素(c),也就是人肉编译器。 当我在自己的程序中,发现用到了模式,我觉得这就表明某个地方出错了。程序的形式,应该仅仅反映它所要解决的问题。代码中其他任何外加的形式,都是一个信号,(至少对我来说)表明我对问题的抽象还不够深,也经常提醒我,自己正在手工完成的事情,本应该写代码,通过宏的扩展自动实现。

(完)