CS巴别塔(1)

Virus Scanning Fail

Posted in FAIL by Kenny Yuan on 2009/08/31

VirusScanFail

It is the real wget.exe, and they already knew.  (http://vil.nai.com/vil/content/v_101106.htm)

介绍一下thefreecountry.com

Posted in Uncategorized by Kenny Yuan on 2009/08/25

这是一个好站,一个不可多得的好站,虽然有了google这样优秀的搜索引擎,但人肉编辑的目录仍然有它的优势,thefreecountry.com就是一个例子。

(虽然它也有RSS订阅功能,但它的风格很怀旧,是我喜欢的那种Golden Oldie的网页风格)。

这个网站包括这些内容:

程序语言

免费的Ada编译器/解释器,免费的Algol编译器/解释器,免费的Basic编译器/解释器,免费的C编译器/解释器,免费的C++编译器/解释器,免费的C#编译器/解释器,免费的Delphi编译器/解释器,免费的Lisp编译器/解释器,免费的Cobol编译器/解释器,免费的Fortran编译器,免费的Logo编译器/解释器,免费的Modula-2编译器/解释器,免费的Modula-3编译器/解释器,免费的Pascal编译器,免费的Perl编译器/解释器,免费的Python编译器/解释器,免费的Prolog编译器/解释器,免费的Smalltalk编译器/解释器……

(上面这一段很有相声大惯口的感觉,对吧?)

程序库

免费的声音、音频、音乐、数字语音程序库和源代码,免费的压缩、归档程序库和源代码,免费的C/C++框架程序库和源代码,免费的调试库和源代码,免费的内存泄漏检测程序库,免费的资源泄漏检测程序库,免费的单元测试程序库,名句 费的加密、解密程序库和源代码,免费的FTP/SFTP/TFTP程序库和源代码,免费的游戏编程程序库和源代码,免费的GUI程序库和源代码,免费的跨平台GUI程序库和源代码,免费的JAVA程序库/Applet/Bean和源代码,免费的数值计算、数学分析、数理统计程序库和源代码,免费的Pascal/Delphi程序库和源代码,免费的PDF开发程序库和源代码,免费的物理引擎程序库和源代码,免费的正则表达式程序库和源代码,免费的Socket编程程序库和源代码,免费的拼写检查程序库和源代码……

编程工具

免费的剪切版增强工具,免费的查找和替换工具,免费的调试器、静态代码分析工具,免费的编译器构造工具,免费的反汇编工具,免费的DOS增强工具,免费的GUI生成工具,免费的帮助写作工具,免费的JAVA IDE,免费的软件更新(software update)工具,免费的程序编写工具,免费的文本编辑器、免费的IDE、免费的正则表达式测试器和正则表达式交互式生成器、免费的安装程序制作工具,免费的资源编译器、资源生成器、资源编译器、图标编辑器,免费的CD Autorun工具,免费的文本比较(DIFF)工具,免费的源代码美化器,免费的版本控制系统,免费的程序员字体、设计字体、网络字体,免费的3D生成器,免费的命令行外壳(CLI SHELL)……

文档和指南

免费的C/C++程序文档和指南,免费的C#程序文档和指南,免费的SQL程序文档和指南,免费的PASCAL程序文档和指南,免费的PROLOG程序文档和指南,免费的声音/音频程序文档和指南,推荐的C/C++书籍,推荐的编程书籍,推荐的Windows/Unix编程书籍,推荐的Socket/Internet编程书籍……

仿真器和虚拟机

免费的X86仿真器和虚拟机,免费的68000(68K)和Power PC Mac仿真器,免费的Amiga仿真器,免费的Apple II仿真器,免费的ARM仿真器,免费的Atari仿真器,免费的虚拟光驱……

嗯,打字打到这里开始手疼了,不打算再翻译了,哪怕这种随便摘译也很费功夫的。一般来说,介绍一个网站的最好方式是给别人看它的Site Map(虽然这样子不适合于小白),以下就是它的Site Map:

Site Map of thefreecountry.com

This page gives a list of most of the pages available on thefreecountry.com.

Free Programming Language Compilers

Free Source Code and Programming Libraries

Free Programming Tools and Utilities

Programming Documentation & Tutorials

Free Emulators and Virtual Machines

Free / Open Source Operating Systems

Webmaster Documentation & Tutorials

Free Domain Name, DNS and Web Redirection

Free Website Design and Maintenance

Free Website Promotion and the Search Engine Optimization

Free Newsletters, Ezines, Ebooks

Topical Articles and News

Web Hosting

Free Perl Scripts

Free PHP Scripts

Free ASP Scripts

Free PHP/CGI Script Hosting

Free JavaScript, AJAX, Dynamic HTML (DHTML) and Web 2.0 Scripts

Miscellaneous Free Webmaster Resources

Free Security, Privacy & Anonymity Resources

Free System Utilities

Free Multimedia Software

Free Graphics Software

Free Office and Productivity Software

Free Internet Utilities and Free Online Services/Applications/Tools

Logtime

Tofrodos

Miscellaneous

VW1.4T与多款2.0自然吸气引擎的工况对比

Posted in Uncategorized by Kenny Yuan on 2009/08/22

引言

当过兵的,打过仗的,都明白一个道理:“新兵怕大炮,老兵怕机枪”。在评价汽车引擎时,也有类似的规律:“新手看功率,老手看扭矩”。

声明,声明,我喜欢声明

首 先要声明的是,将Turbo引擎和自然吸气(NA)引擎放在一起对比是非常不公平的。哪怕Turbo机的排量小一些,对自然吸气引擎也是不公平的。因为 Turbo相对于自然吸气,有着工作原理上的先天优势——较高的热效率(了解热机原理的朋友会明白它有多么地重要)。而且从实践上来说,刷一下Turbo 引擎的ECU,就可以很容易地“白白获得”几十匹的马力和几十牛米的扭矩。所以,这种对比应该是放在厂家的产品定位基础上的。如果有不同厂家的两款车,其 产品定位基本相同,构成了竞争关系,价钱也在同一水平上。只不过其中一款是2.0升的自然吸气引擎,而另一款是1.4升的增压引擎,这才算是大约有了可比性。

自 然吸气引擎如果想要追求高功率,需要做到高转速下的进排气顺畅,所以需要多气阀。但是在低转速下,多个排气门会造成“漏气”严重,扭矩不足。故而迷恋自然 吸气引擎这个传说的厂家,纷纷发明了可变进气歧管、可变气门升程和正时等多种方案(尾注)。这种机构能够同时兼顾低转高扭和高转高功两个特点。只不过,这 种机构在中、低速时的作用,在Turbo面前就没有任何优势了。本文要做的对比,主要就是讲在一般驾驶工况下(比如4000rpm以下),VW的1.4T 引擎的优势。

误区,误区,我纠正误区

在具体解读工况图数据之前,还要纠正一个常见误区,也就是关于“涡轮介入”的误解。 实际上,Turbo机头是没有所谓“涡轮介入”一说的,这个说法本身就是不了解Turbo原理之下的错误产物。发动机的进气系统必定经过 Compressor Wheel,发动机的排气系统必定经过Turbin Wheel,只要引擎开始转动,增压器内的增压部件就开始转动了,这时增压器就开始工作了,这时候引擎使用的空气也就是增压过的。 Turbocharger本身并不包括离合器装置,也就没有在某个转速突然“介入”一说。反倒是在增压压力过高时,有一个泄压阀(Wastegate), 用来将多余的废气从旁路排出,避免增压值过高。泄压阀有机械式(在胡克定律下工作)和电磁式的两种,电磁式的一般用ECU控制。之所以Turbo引擎刷 ECU能够提高功率和扭矩,秘密就在于这个泄压阀的控制——晚开启就可以增加增压值。

上图,上图,我喜欢真相

著名的孔子曾经说过:“一幅图胜过十句话,无图无真相”。让我们先来上一个对比图:

VW 1.4T vs 2.0 NA Engines

VW 1.4T vs 2.0 NA Engines

VW有几款不同的1.4T引擎,能够找到详细数据的是103kW版本的。上图中的数据就是这款VW1.4T引擎和一些2.0NA引擎的功率/扭矩图(其它引擎只画出了扭矩曲线)。其中绿色和暗红色的是VW1.4T的引擎的实测工况图,灰色是官方宣布的工况图。从图中我们可以读出:在引擎1147转的时候,扭矩有178牛*米,在1286转的时候,扭矩升到了195牛*米,在1481转的时候又升到了210牛*米。从趋势上看,从1200转到5000转,扭矩一直在195牛*米以上。虽然这台被测引擎没有达到官方工况图中的那个扭矩平台——从1500转到4000转都一直提供最大扭矩220牛*米,但这样的扭矩输出,仍然是能够让人非常满意的。

图中的其它引擎都是自然吸气引擎。虽然排量为2.0L左右,但是扭矩就比这款VW1.4T的增压引擎差多了。比如表现最好的那条鲜红色的曲线,代表的是2006年款型的BMW 320i 2.0升NA引擎。从图中可以看到,某台引擎的实测数据为:最大扭矩180NM@3000转(BMW官方宣称最大扭矩200NM@3500转),在中低转速下,此发动机的表现要比VW1.4T差上许多。不将油门轰到6000转以上是占不到什么便宜的。

总的来说,这些2.0NA引擎相比VW1.4T,不但最大扭矩峰值偏小,而且出现最大扭矩的转速普遍都很晚(3000rpm以上)。虽然有些引擎的功率很高,但都是通过高转速来实现的,4000转或者5000转以上的转速,对于普通驾驶并没有多大的意义了。弹射起步、跟趾换档、转速不下7000、过弯找刹车点的那种驾驶习惯,应该出现在赛道里。而且普通的民用车也满足不了这样的激烈驾驶用途。

国产,国产,我支持国产

VW在国内生产了一款96kW/220NM不带Supercharger的1.4T引擎,它的低速扭矩略差,比如在1000转时大约为140牛*米多一点(比带Supercharger的少了30牛*米);但是在1600转到3500转时,它仍然能够持续提供220牛*米的峰值扭矩平台;在高转速下这款引擎的扭矩表现略差于103kW的那一款(这也是为什么它的功率较小的原因),在5000转时还有180牛*米,在6000转时就只剩140牛*米了……综合来看,在中低转速下,这款1.4T 96kW引擎虽然略差于103kW的那一款,但是却仍然远远领先于图中的其它2.0升引擎。

哼哼哈嘿,快使用DSG

说引擎的贴子里说到变速箱应该算是跑题了。但VW的这款七前速DSG变速箱和1.4T引擎都快成了伴生的双星了,不得它一句也显得很不厚道。好了,现在提过了,图也上了,这一段也就可以结束了 🙂

VW's 7-Speed DSG from Luk

VW's 7-Speed DSG from Luk

资源推荐

最后,推荐一个网站,howstuffworks.com,看名字就知道它的内容了。许多东西的原理都解释得很清楚,讲得也很深入。这个网站我向别人推荐了5年了,但是没发现有多少人真的去看——嗯,你猜对了,内容都是英文的。

——–
注:虽然本田吹它的VTEC吹得很早,而且这十几年来一直在吹。但VTEC本身并不是什么先进的技术,基本上所有的厂家都有,其中做得最好的是BMW,正时和升程都是无级可调的,还带可变进气歧管。而本田的那种则仅仅是两级可调。对于本田这种厂家生产的中低档车,我一直没什么好印象,以下是另一个例证:在2008年第八代雅阁上市的时候,本田在国内竟然在吹嘘它“使用了副车架”,还说这东西有多好多好——要知道,副车架可早就不算是什么先进设计了,许多低价的小型车也早就装备了,比如7年前在国内上市的POLO就有副车架了,而且还是用铝合金和高强钢做的。

Tagged with:

IEEE浮点数提高班

Posted in Uncategorized by Kenny Yuan on 2009/08/19

谁是本文的读者

本文名为提高班,所以它当然是写给初学者的。高手以及自认为高手的请勿视。如果不确定自己是否需要观看,这里提供一个小测试:但凡不会回答下面的题目的,建议你阅读本文及末尾提供的延伸阅读材料。

先来看这一段程序,如果x是C++内建的数值类型,在什么时候DayOfReckoning()函数会被调用呢?

if ( x != x )
{
    // WTF ???!!!
    DayOfReckoning(x);
}

这个题是考计算机数值表示的。答案是:当x为浮点数的NaN的时候。(再多说一句,用memcmp比较“是否相等”是靠不住的,因为有NaN的存在)

NaN,它不是一个数

NaN是Not a Number的缩写,就是说它不是一个数。NaN是定义在IEEE 754标准中的特殊值,类似的特殊值还有INF(无穷大,INFinite)。IEEE 754是定义浮点数的标准,有1985和2008两个版本。其中,2008版本还定义了十进制浮点数的标准。

NaN的产生(可以)是这样的(此段文字摘译自WIKI):
1)NaN参与数学运算的结果仍是NaN
2)0/0,正无穷大/正无穷大,正无穷大/负无穷大,负无穷大/正无穷大,负无穷大/负无穷大
3)0和正/负无穷大相乘
4)正无穷大+负无穷大,负无穷大加正无穷大
5)数学上无解的一些函数值,比如:负数的平方根,对2.0的求反正弦,等等

第一条说明了NaN在运算中的“传染性”。如果不慎在某处引入了NaN,那么之后的算式值很可能就一直是NaN了。所以在涉及浮点数值运算时一定要注意NaN的存在。

回过头来解释前面的题目:NaN永远不等于自己,所以说NaN == NaN永远为false,而NaN != NaN永远为true。这是一个相当特殊的情况:即使参与比较的两个NaN的内存表示是一模一样的,但它们仍然是不等的。

更深一步地说,NaN有两种,一种是Quiet NaN,另一种是Signalling NaN。在使用时,Signalling NaN会立即引发异常,然后将自身变为Quiet NaN;Quiet NaN的行为比较安静,在算术运算中它不会引发异常,只是将运算结果“传染”为NaN(但是在某些不接受NaN的地方仍会引发异常)。

INF,它溢出到无穷大

INF就是无穷大,在浮点数中,有+INF和-INF两种。INF在数学上有对应的概念,所以它比NaN更好理解一些。比如用1/0,得到的结果就是INF

0,它竟然还有正负

在IEEE浮点数中,0也是特殊的,因为它有+0和-0两种(“正零”和“负零”)。其中,1除以“负0”等于“负无穷大”,1除以“正0”等于“正无穷大”。

舍入,它可不是“四舍五入”

舍入算法对我们多数人来说意味着“四舍五入”。这个在上学时就学过了,但它却会给舍入之后的数带来扩大的趋势。在买卖方之间,民俗传统中一直有“五刨六撩”一说,“五刨”是说将5及5以下的舍去,“六撩”是说将6及6以上的进位,这种舍入算法有整体减小的趋势,体现了小生意人让利的“诚意”。那么,更好的舍入算法是什么样的呢?

IEEE754中定义了最基本的舍入算法:Rounds to nearest, ties to even(even在这里是“偶数”的意思)。这种算法将4及4以下的数抛弃,6及6以上的数进位,如果要舍入的最高位是5的话,则将要保留的最低位圆整到最接近的偶数(这里说的“偶数”包括0)。比如:

89.64 --> 90
98.46 --> 98
1919.54 --> 1920
1918.55 --> 1918

对于大量均匀分布的数来说,这种50%概率算法保证了舍入后的数没有放大或者缩小的趋势。在银行里都是用的这种算法,做过金融类程序的对此应该印象深刻的。

除了上面提到的“Ties To Even算法”之外,还有几种舍入算法,因为定义得非常清晰和明确,这里不再多说,详情请见参考资料。

总结

即便是使用高级语言,只要使用浮点数,就有可能碰到上面提到的问题。可惜国内的教材多数只讲了浮点数最基本的存储形式,对于上面几个很重要的话题竟然只字未提。本文存在的意义也在于此。据说,每在文章中加入一个二进制表示公式,文章的读者就会减少一半,所以我没有放任何的公式。感兴趣的可以用鼠标猛烈敲击参考资料/延伸阅读。

参考资料/延伸阅读

1、http://en.wikipedia.org/wiki/IEEE_754-2008
2、http://en.wikipedia.org/wiki/IEEE_754-1985
3、http://en.wikipedia.org/wiki/Signed_zero
4、http://en.wikipedia.org/wiki/Not_a_Number
5、IEEE标准

致谢

感谢Jun Yang指出文中的问题(http://blog.csdn.net/lovetheme)

1990-1995年间学习编程语言的回忆录

Posted in Uncategorized by Kenny Yuan on 2009/08/17

绝对必要、又臭又长的作者声明:
1、虽然是回忆录,但本人是码农不是艺妓,想来看艳情的可以洗洗睡了。(推荐你看《往事回忆录》,那个很艳情,而且不黄不暴力)
2、我不是成功人士,想看成功人士的奋斗经历来励志的,可以关窗口了。(推荐你看《小兵传奇》,那个很成功、很励志)
3、我没开发过大项目,想看内核技术和巨型软件构建经验的,可以打酱油了。(推荐你看最近出的写WinNT故事的那本翻译书,不过我还没看过,只是听说不错)
4、我没有高学历,想围观高学历的地球原住民的,可以飞过、飘过、走过、路过、虫洞过了,这里没沙发。(推荐你看《新语丝》,有闲心的还可以去找找,看到底在加州理工的CS博士列表里面有没有唐峻)
5、写回忆录不是为了要显摆什么,记忆中是什么样就怎么写(当然,记忆重构是无法避免的)。本人绝不像某些国内伪专家一样装B,别人说什么东西好的时候,非得过来插一句说“我XX年前就见识过了”。那种人我最讨厌了,所以本文绝无半点那种倾向。所以,如果想跟我学装B也不要往下滚动了,我这人的特点是有一说一,认为自己好的也说,认为自己差的也说。(装B最好的方法是把一些人人尽知的东西,另一群不知道的人中传播,最好自己写BLOG来传播,参考经典著作而不要直接抄袭,这样最能装B!)
6、本人今年30岁,在1990年的时候我11岁,科特学习机也已经上市,已经具备了学习计算机的生理上的可能性,和客观条件上的可能性。各位福尔摩斯和柯南们可以回家了。

Basic

Basic是我编过的最多程序的语言,没有之一。虽然后来工作后也写过一些30-50MB CPP源码的项目,但我一直坚信BASIC是我写过最多程序的语言(大约是因为易得性直觉吧,估计真实数据不是这样的)。当时的BASIC是我自己唯一拥有的计算环境——科特学习机的F-BASIC。Basic是我见过的第一种编程语言,在我还没学会打字的时候就开始用它编程了。当时最大的愿望是写一个游戏程序。现在回想起来,这算是家长禁买游戏卡带给逼出来的发奋图强了。

当时的F-BASIC里有背景图可以组成画面,有超级马利中多数的卡通角色可以用程序控制。编写一个游戏程序从技术上看起来是可行的,而且我还有大量的时间,没有资金预算的问题——虽然这个项目的可行性评估完美通过,但最终还是理所当然地失败了,这是我人生中的第一个失败的软件项目,住在100公里外的表弟最终也没有能够玩到我写的游戏。原因倒是很简单:F-BASIC没有BASIC中的陷阱编程(一种类似于中断的东西,可用来捕获键盘输入),无法及时控制卡通人物行动,运行速度也太慢。这件事情的“蝴蝶效应”直到现在还余波未消——在讨论可行性的时候,我总是习惯于搞清楚“是不是真的可以”,不满足于从文字中求证,而是要测试过才放心。(当然,从另一个角度来看,所有的技术问题最终都是管理问题,这就是另外的话题了)

虽然游戏项目可耻地失败鸟,但收获还是有的。最大的收益就是学会了将问题转换为计算机表达,比如讨厌的约瑟夫问题,我一直不会用公式计算,都是在纸上画圈圈和叉叉,挪到计算机上还是挺节约白纸的;小学奥赛的经典名题“鸡兔同笼”在穷举法面前也不堪一击(虽然有无数求解公式,但我一直记不住);在《世界数学名题趣谈》上看到的那个可以计算PI值的无穷展开的算式,我也是没有用笔算的耐心的(如果让我也算出几百位PI之后发现出错了,同样也会吐血的),所以,写个分数运算程序,用计算机来运算还是很爽的——虽然当时的字串最长仅允许32字节,即使拼起来用,最终算不出几位有效数字。

在这种简陋的F-BASIC中,我还体验了一把“多媒体编程”。使用F-BASIC中的PLAY语句,播放三个声部的伴奏乐谱,然后录下来给音乐老师作教学用,作为交换,我可以弹她的电子琴。因为直接写那些不断重复的“CDEFGAB”唱名字串很繁,我开始用程序来生成字串,这是我第一次接触到程序和数据之间的相互转化问题。但很遗憾地是,F-BASIC不能由数据生成程序(如果有向磁盘LOAD和SAVE的标准BASIC倒还可能,F-BASIC是向录音机存储数据的)。后来当我见识到了eval之后不由得大喜过望——数据向程序转化的东西终于有了。

除了这些之外,BASIC对我的负面影响也很大,比如良好命名,模块划分等方面,它带给了我许多不好的习惯。这些在后来学习LISP的过程中阻碍了我很长一段时间,后来才慢慢改掉。记得有句名言说第一个学BASIC的程序员不会是好程序员,但我感觉这话太过绝对了。或许它表明了一种趋势和倾向,但并不是一定如此。人,长着脑袋,就是用来学习和改进的。

dBaseIII

中国最早的信息化就是用的这玩意,无数个单位买来“先进”的286微机就是为了“会计电算化”或者“电子信息化”,所用的工具九成九都是dBaseIII。当时的教育台里也总会有一个戴着龟壳大眼镜的谢顶老大爷,操着难懂地普通方言话,来讲解dBaseIII。我一直没有把dBase系列看成一个真正的计算机语言,最初以为它是数据库,现在觉得它是个带一定编程手段的查询语言。当时听说它比COBOL要先进不少,所以为了迈向“高大全”的目标,就把它顺便也学了。后来的《跟我学电脑》丛书,第四册整本讲的都是dBaseIII,我翻了无数遍,在纸上默写语法和程序,并且将样本数据进行了演算,最终搞明白了这种“语言”。借着为某些单位修理dBaseIII系统的机会,我“偷”到了不少上机时间。另外,在纸上推演dBaseIII数据的时候,也被迫学习了索引/二分之类的基础知识,认识到了算法和数据预处理的重要性,为后来学习《数据结构》等打下了基础。

Lisp

Lisp一直是我最喜欢的语言,没有之一;但LISP也是我一直没有能够掌握的语言之一(这回有“之一”了)。

我是在《跟我学电脑》那套从书中的《计算机语言发展历史》章节中知道LISP的,而且听起来它有一个很牛B的名字——人工智能语言。当时的中央电视台的译制片栏目放了一部195x年的美国电影《地球停转之日》(不是后来Keanu Reeves主演的那个翻拍版本),那里面的机器人功能很牛B,而且能听懂外星主人的语言,甚至人类小妞学着说的时候,它还知道把人类小妞给保护起来——我当时就认定这机器人一定是人工智能的了。于是就买了LISP的书来学习。这本书里讲的是Sun工作站上的Common LISP,还附带讲了LISP机的辉煌历史——这让我后来很自然地就接受了“用IDE工作”的理念,而且在相当长的一段时间排斥命令行编译(直到后来学了GNU MAKE才改正过来)。当然,对于LISP机关掉GC等待内存耗尽的故事,那本书里是不会讲的。这也是中国“学者”的通病,如果他说什么东西是好的,就只说优点不说缺点。这一点读书时一定要注意。

LISP对我来说的一大特点就是自然,计算机不像BASIC那样地讨厌,而是变成了一个很自然的描述工具。现在想来,这和LISP的历史渊源是有很大关系的——LISP最早就是John McCarthy发明的一种数学符号,而不是一种编程语言。只不过他的一个学生偶然在IBM 704机器上实现了可以解释执行的计算机语言LISP——关于IBM 704 blah blah这一大段是我死记硬背的,我这辈子还没见过这种机器,估计也没机会见识了。

正因为LISP的原始发明意图就是在纸上推演的数学符号,所以使用它的时候完全感觉不到太多的障碍,这种障碍在别的语言中有很多表现,这里就不一一举例了,相信大家都有体会。LISP的这个渊源,对当时的我来说,还有一点有着非同寻常的意义:它可以在纸上演算并求得结果,求解过程是可以直接用人类的大脑来做的,而不像C语言那样几乎不得不用机器语言来做。举个例子,虽然CAR这个函数名称的由来和寄存器有关,但是它却有着非常直观的含义——砍掉LIST中的第一个元素:(car ‘(1 2 3)) -> (2 3)。至于为什么我得在纸上演算,原因很简单:在那段时间里,如果一个中国的初中生能够接触SUN的小型机,那他一定是牛B到不能再牛B的人物——很明显我不是,所以我只能在纸上演算。

虽然LISP很直接和自然,但当时的我只有初二数学水平+一门粗浅握的《逻辑代数》,又没有太多的编程实践经验,所以对于某些东西的掌握还是很困难的,比如“闭包”这个怪物的意义,就让我困惑了很多年,直到后来看到了它和对象的等价式后才恍然大悟)

学习LISP对我的影响很大,从小的方面来看,有“看得见的万能之手”——完美的MACRO系统;递归——人来负责简洁清晰的算法,机器来负责提高运算效率;GC——白胡子老管家式的周到服务;程序和数据的大一统——后来知道这叫S表达式;IF-ELSE——这个也是LISP的发明人John McCarthy在LISP里发明的(不是ALGOL 60),嗯,我承认这一条基本上是用来刁难人的;从大的方面来看,学习LISP让我开阔了视野,在后来的许多年里,都只把C风格的语言当成低级语言,而没有陷入语言成瘾的状态,对于2000年以后流行的各种语言也持有一种积极接受的态度——本来这些就都是从LISP里拿来的嘛!记得有一句名言,大致是说:1、世界上只有两种计算机语言,C风格的和LISP风格的;2、一个现代语言的发明,就是在C的风格基础上借用一些LISP的“先进”概念(比如精通LISP的James Gosling发明了带有GC的JAVA)

Prolog

最早知道Prolog还是在《跟我学电脑》那套书里知道的(若干年后才知道是自大的法国人强行发明的),当时书中称这种语言是“下一代的计算机语言”,是计算机业的未来。为了“不输在起跑线上”(当时很流行这句话),我立即就去把书买了回来,虽然不时地翻出来看,但一开始根本就没看懂;而且这本书上说:对于学生来说,同时学习Prolog和LISP是很难的事情,因为风格不统一。我当时在学LISP,所以Prolog就放弃了好长一段时间。

后来在某一天,偶然翻看Prolog的时候,我突然间就顿悟了(不过我没有像阿基米德那样光着屁股裸奔)。比如在Prolog里定义一个逻辑关系,根本不用像BASIC那样绞尽脑汁定义变量存储,也不用像LISP那样编写函数去递归求解,如果我说farther(A, B).的话,那就是有一个A是B的父亲,至于A和B是什么,Prolog不知道、也不必知道,甚至对于father这个谓词它也不知道,它只知道这其中的逻辑关系。当然,如果仅仅能够接受这样的输入,也没什么意思,但Prolog可以在这些云山雾罩的关系的基础上,继续定义逻辑关系,并且最终还能自动求解,比如:定义一个grandfather,就可以在father的基础上定义谁是谁的爷爷(多说一句:Prolog默认没有交换律——请按群论来理解这个“交换律”的意思)。

过去学的种种语言,无论是LISP、C++还是BASIC,编程的人都得替计算机想出如何求解的过程来(不论看起来复杂的迭代,或者看起来简洁的递归,其实都是在定义求解的过程),而且往往还得发明一些东西来帮助计算机:比如用BASIC这样的东西来求解约瑟夫问题,还得用自定义规则,用来模拟语言本身不支持的链表结构。但是Prolog就不一样了,它就像一个解题机器一样,你告诉他一堆事实和规则,然后就可以提问了——完全不需要告诉它解题的“过程”。Prolog其实也并不知道答案“到底是什么”,但是它可以按照你定义的规则求解出你所需要的答案(可能需要多打几次分号)。至此,我发现我的逻辑代数白学了,学一个简单的Prolog语法就全部解决了嘛!

从那以后,我一直疑惑为什么LISP被称为人工智能语言而Prolog不是。而且“谓词”这个概念被我深深地记在了心中。到后来听说ISO14882 C++98标准中竟然有谓词Pred,我着实兴奋了好久,但后来仔细一研究,原来跟Prolog的完全不是一回事。不过,C++模板编程在某些方面来看倒是很有Prolog的风格,这也是有一段时间我不自觉地陷入了“template误区”的原因之一。

C++

借一句当年流行的歌:“你这样,一个语言,让我欢喜让我忧……”。最早我学C++仍然是因为《跟我学电脑》这套丛书,那里面介绍了B语言转化为C语言,然后升级为C++语言(根本没提和UNIX的关系!)。既然要学,那我就学一个最好的吧!于是就略过了C,直接学的C++。所以在之后的10多年里,我一直很鄙视“学C++必定要先学C”这样的说法。当时买的是张国锋写的C++的书,大约是91年版或者92年版的,只是在讲语言,没有讲编译、链接和调试。因为我是BASIC出身,又喜欢LISP,所以在“如何将程序变为机器码”这一环节是一直是缺失的,直到后来整了一套Visual Studio 97(VC++ 5.0)的光盘才真正开始熟悉整个的编译-调试过程。我还记得第一次碰到“找不到链接符号”这个错误时,我用了好几天才搞明白是函数体忘了写。在这方面的知识一直仅仅是够用,直到后来在做过一个集成开发环境(IDE)后才得到了加强:许多问题都是在解析过OMF文件信息之后才真正理解的。因为自己有这一段经历,所以我在教别人的时候一直强调:第一天就必须要上机来熟悉开发环境。懂得如何生成代码和调试代码,是作为一个编译型程序员必不可少的技能。

C++的教材在讲class的时候喜欢用复数(complex)作为例子。对于当时只有初中二年级数学知识的我,这就不啻于天书了,许多年一直没能明白虚、实分量的相加代表了什么。还好里面有一个动物的比喻能够帮我理解OO的概念。但也仅限于封装、继承、多态等基础知识,很长一段时间我没有意识到C++是多范型的程序语言。后来我学习C++开始转向“实用”这个“目标”(其实是学Windows SDK):先是学习了MFC,然后开发过许多控件和程序,帮老师做过学校的课题,往杂志投稿过一些VC程序,大约在99年左右,我开始有些飘飘然了,觉得C++不过尔尔(其实是连C++和VC都没分清)——这种心理一直持续到大约一两年后,有一次偶然在CSDN上看到某装B人士,正在以极为不屑的态度批评一个发贴者不懂“面象对象”,他祭出的武器就是OCP。经过google得知OCP的内容后,我不由惊叹“此为神器也!”,继而开始拜读Bertrand Meyer的《OOSC》,从此再也不敢“翘尾巴”了。之后的许多年里,我一直在观察周边的人,发现许多人的OO教育从一开始都是错的。我认为这不仅仅是我本人的一个教训,也是教育的失败:当你让学生接触到某种东西的时候(例如OO),只给他看一些零碎的基础概念(封装、继承),教一些基本的实现手段(语法),从来不说这种东西的思想精髓在哪里(OCP/LSP/etc.),也没有提供任何延伸阅读材料,反倒是在一个封闭的小圈子里不停地说这东西好,这东西就是某个样子的——事实上这个“样子”却违悖了其中的精髓思想,而且一说就是十多年不间断(因为教材是抄来抄去的?)。若干年后,某些曾经被错误教育的有识之士终于意识到了问题,从国外重新引进了原本的精髓思想。但也已经无法成为主流了,错误积累的太过深重,OO本身已经完全毁掉了。

说到C++几乎不得不说《设计模式》。国内的中文译本大约是在2000年出版的。在当时设计模式很受人们推崇,给人一种万能药的感觉,仿佛有了它中国就不必再担心软件结构的设计问题了。在我看来这又是典型的“大炼钢铁”式的思维——一味地追寻成功者的足迹,不管是不是充分条件(甚至不管是否必要)。我一直觉得设计模式在国内吹嘘得过分了。从OCP等原则出发,可以推出许多模式;从LISP语言看过去,许多模式就是一陀额外的屎——但是这个想法一直不敢太公开(人微言轻啊),直到后来读到Peter Norvig的那个Presentation。

C++的98标准及相关的一些知识是在CSDN论坛上得知的,后来疯狂地购买和阅读C++的相关书籍,几年后名单上的差不多有20本书以上。至此,算是重学了一遍C++(书单中还真包括一本《C++ Primer》)。看过之些书这后,在之前的OO的基础上,明白了对象模型的细节,熟知了STL和模板的应用(甚至有超级古怪的应用),熟记了一些常见的陷阱和Workarounds,甚至还背诵过三字节操作符这样罕见的东西。现在回想起来,学习这些东西的机会成本,其实还是蛮高的。

古老的C++语言因为泛型编程而焕发了“第二春”,但我对C++语言的兴趣却开始慢慢消失。虽然一直还在关注着这一领域,但也只是在关注了。从网上随处可见的关于C++的争论之中,我能体会到一些人不是因为理智,而是因为某种情结和瘾癖而在捍卫着C++,他们的思维陷入了一个怪圈中。一开始我觉得这是因为见识的问题,后来意识到,归根结底还是因为open mind的问题。等到了“否定C++以外的东西”成为了你的第一反应的时候,或者当你总是下意识地寻找和传播C++的好处的时候,你的大脑很可能就开始封闭了。这是一件很危险的事情,而且更危险的是你自己很难意识到这种状态。有一段时间,我曾经几乎要陷到这种状态当中去,幸运地是,偶然有一次聊天时说到LISP,在我向别人介绍LISP的强大的同时,我突然感觉到天灵盖处的一阵颤栗,仿佛冥冥之中的设计好的某段程序重新启动了,十年前学习各种编程语言时的感受重新涌入了大脑,迫使几乎侵占了所有地盘的C++重新回归了它应该有的位置。后来LP推荐《心理学》给我看。在此之前,我曾经因为一本特别差劲的教材,而误解心理学为伪科学——这一错就是十多年!在LP的推荐之下,我看了David Myers的《心理学原理》,才终于接触到了真正的心理学(其它好书也有一堆,这里同样就不列举了)。学过了心理学之后,我对自己当时的心态也就有了一个更加清醒的认识。我决定把这些写下来的原始动机,也是为了总结+自醒,也供他人借鉴。

Pascal

将Pascal写进来有点时间线的问题,因为Pascal是在上高中为了参加奥赛而学的。但既然上面讲C++时,已经说了后来发生的事情,这里也一并说一说吧:Pascal语言,我也是对着书在纸上啃下来的,没有上机。有了前面学过的那些语言作为基础,学Pascal只用了不到一周的课余时间。这个事情对我的影响却是负面的,有很长一段时间,我错误地以为学习一门新语言是简单地事情——有了原来打的基础,学习语言本身的确是很简单,但是我却忽略了程序库的重要性。我一直不会JAVA语言就是因为它的语法太平淡了。虽然我在上大学时,也有过一本1000多页的《JAVA内裤大全》,但JAVA还是被我给错过了。

(更新:昨天浏览了一下,发现写出来一个大BUG,看来狂打字的时候脑子果然是不灵光的,但既然这么久过去了,BUG我也不改了,留着给人用来挑错吧,呵呵)

Tagged with: ,

本应该写在twitter的一句话

Posted in Uncategorized by Kenny Yuan on 2009/08/03

有一天,LP对着我大吼:不许讲道理!

于是,她赢得了那场辩论的胜利。