2008年12月16日星期二

emacs简介

emacs一定是我梦想中的编辑器,设计者一定是个超级的混蛋。
emacs是一种多平台的编辑器,具有非常古老的历史,和vi一起并称为黑客常用两大编辑器(在贝壳学习linux后,就“顺带”学习了vi——因为根本没有其他选择)。作为编辑器来说,他只接受文本编辑,但是却具有收发邮件,编译软件,调试程序,甚至煮咖啡等各种诡异的非正经功能。很多人疯狂的热爱他,认为他是人工智能的结晶,化妆成编辑器的操作系统。但也有人疯狂的咒骂他,认为这东西纯粹就是折磨人用的。贝壳这次的blog,就是侃侃emacs到底是什么。
说emacs是人工智能平台,其实这个说法并没有错。emacs的运作机理和我们通常的编辑器都有不同。通常来说,一个编辑器可能有多个windows 啦,frame啦什么的,但是最终作用于一个文本。你可以添加,删除,修改,或者标记一段文本,进行剪切,复制,粘贴等动作。设计比较完善的还有回退和重做。让我们专注于其中一个任务,例如,删除,看看我们的编辑器是如何工作的。
以notepad++为例吧,在选中一段文字删除的时候,你可以用键,同样,如果没有选中文字,这个键作用于当前光标后面的一个文字。(在此我们不讨论和前后删除的关系)这很简单,一个内容,按删除,就没了。但是你是否注意了这个过程本身,为什么按下是删除,为什么不是按下键或者< a>键?
按照程序员的思维来说,编辑器的基础是一个文本内容和一个光标(或者说一个position),我们通过修改数据结构变更这两者以达到某种目的,这个过程被称为操作。例如,作为删除操作,我们移除(remove)了文本内容的position处的字符。而后,我们将这个操作(也可以称为函数)绑定到键上。于是,我们在按下键的时候,触发了删除函数,导致内容被删除。这个是能够删除文本的核心过程。从正常人思维的角度来说,一般我们都会将这个功能绑定到上面,而不会是或者,或者其他更疯狂的键。同样的机理,我们按下,文件存盘,是因为存盘的功能被绑定到了这个键上。好的编辑器一般带有键绑定修改功能,notepad++就可以自行修改热键。而emacs则更进一步。
emacs允许你自行编写扩充函数,并且将这些新的函数绑定到键上,这样就赋予了编辑器无限的可能性。例如,你可以写一个过程,每次触发就在当前光标处插入当前时间对应的纽约时间。或者你可以写一个过程,自动根据一个预定义的数据表格补充你输入人名的头衔。可以想象,当你需要重复单一工作时,这种扩充能力是非常重要的。你可以免除记忆一堆领导的详细头衔,免除重复输入,免除繁重的劳动,只要你编写一次扩展程序,并且绑定到某个键上。然而现在编辑器的趋势是,我们使用编辑器从事各种不同的工作。有的时候,我们用编辑器记事,有的时候用来写程序,有的时候用来看源码。因此我们对编辑器的个性化能力和强大都没有要求,反之对编辑器的标准化要求很高。说的更通俗点,我们不需要自行扩充一个插件来省事,但是我们一定要用来复制,用来粘帖。因为我们(指普通用户,而非以电脑为生的专业用户或者是变态的geek)不会程序,或者不喜欢为了某个目地花费时间来编写程序,毕竟现在不是70年代,当时接触电脑的都是智力最高的一帮变态。现在接触电脑的都只是普通用户而已,我们不需要强大的扩充,但是我希望我用一个编辑器的时候,这些基本功能在另外一个编辑器上不会产生区别。从这点来说,emacs差劲透了。
emacs的键绑定是根据上世纪70年代unix下(那时候linus都没出生,何况linux)的键盘来的,因此emacs假定你有一个Meta键。这个键在今天的电脑上已经找不到了,我们用Atl来替代。但是同志们,Atl是系统键,这么替代是有副作用的。例如自动补齐的函数热键是M-Tab,但是请试试在windows下按Atl+Tab。亲爱的,你会跳到另外一个程序上。那是windows切换程序的热键。还有我们经常用来复制,来退回。可是在当时的linux下,代表终止程序运行,代表挂起程序到后台。emacs当然要避免这两个热键,于是——你自己试试在emacs下按这两个热键的结果吧。
从热键约定的角度说,emacs是当之无愧的最差编辑器。不过这个很难怪罪emacs,毕竟他出生的年代不用说windows,连dos都没有出生,cp/m还只是个样品。要怪只能怪windows的设计人员在考虑热键的时候根本没有考虑emacs的现有标准,自己瞎设计一通(尤其是最常用的,虽然从人机工程角度这个是最合适的键)。
emacs更强大的特性是,可以根据当前文件的特征鉴定文件类型,并且采用正确的模式。例如,我可以在python模式下编写python程序,在C++ 模式下编写C++程序。对于用户来说,这两种模式看不出区别,然而他们本身有着非常多的细节不同。例如,在C++模式下,/**/是注释,而python 下,#才是注释。灵活的模式允许你使用同样的方法操作不同类型的文件,并且还具有各种扩充。对于C++,他可以编译,对于python,他可以校验。并且有一些比较常用的超级扩充,例如etags。这个程序可以用来生成一些文件,帮助你找到一个符号的位置。利用这个扩充,你可以快速的寻找符号位置,自动完成,等等。这些近几年才在VS IDE和eclipse里面出现的特性,早在数十年前就出现在了emacs里面。
如果你有程序基础,并且长期从事相似的工作,例如写程序,写文档,并且有很多重复的工作,希望解放自己的劳动力,那么推荐你使用emacs。如果你是个找酷的新新人类,希望找一个很少人用的编辑器,具有真正酷的特性,被很多人称赞,那么建议你用emacs。除此外的人,请珍爱生命,远离emacs——这东西太容易上瘾了。

2008年11月23日星期日

竞价排名和不作恶

前两个月贝壳才刚说到百度的竞价排名,果然,这回又出问题了,而且还出的很好笑。
央视曝光了百度竞价排名中的一些问题,主要是有很多医疗信息,百度并没有核实来源。此后,百度总裁李彦宏声称,法律没有要求百度对付费信息负责。从法律角度说,这是对的,我们今天说的主题也不是他,而是这个(http://www.cnbeta.com/articles/69964.htm)。
本来曝光百度,怎么转眼变成google了?
看来百度不应该叫搜索引擎公司,而应该叫公关公司。前两个月讲三鹿问题,他是公关。央视曝光医疗问题,他是公关。现在出这个,还在公关。不过你可以公你的关,不代表股东会买你的帐。详细情况大家可以看这里(http://realtime.zaobao.com /2008/11/081120_21.shtml)。
估计我这篇blog的百度排名应该会很低吧——
下面贝壳废话一下,讲解一下竞价排名的问题,google的价值观和策略。
竞价排名在前两年是一个非常好的模式,通过竞价本身,我们就可以发现很多有价值的信息。例如,我们在搜索IBM的时候,肯花钱的蓝色巨人总比不肯花钱的国际大嘴(International Big Mouth)来的有价值吧。然而问题在于,由于搜索引擎价值的外在性很大,又没有监管,搞不好就要出问题。而且往往不是竞价排名供应商出问题,而是上游下游出,他们没法管。首先我们说外在性的问题,所谓外在性,是指由不应当承担后果的人承担后果的一种状况。好比我在XX地开了一个工厂,生产在欧洲要花很多环保费的东西,破坏了当地的环境。我获得了收入,但是后果由当地人来承担。不论出现的原因,由于外在性的存在,会破坏社会公平,因此很多国家都有补偿外在性的措施。例如排污税,针对富人的高所得税等。竞价排名的外在性在于,有人花钱买排名,并不总是发现价值的过程,也可能是减少价值的过程。而减少价值的损失并不总由百度承担,而是由百度的用户承担。更麻烦的是,这个过程是不可监管的。
我们举例详述整个过程。假定有人在百度竞价买了“流产”(这也是百度最贵的排名)这个关键词,那么,什么人会最乐意去购买呢?我们分析一下流产的潜在市场。正规医院的流产总要通过手续,未成年需要父母签字。很多有钱的小孩宁可多花钱也不希望父母知道,因此他们会选择一些非正规的医院。于是,这些市场一般都是非正规的医院把持的,因为正规医院的收费公开固定,流程有一定监管,肯定没法和这些非正规医院去竞标这个关键词。那么非正规医院中,我们可以想象,应当是付出最高价格的人能够获得这个关键词。如果你按照百度的去,那么你去的地方一定是市场上拥有最高的成本收益比的地方——因为只有这样他才能标到百度的关键词。问题是,什么样的医院会拥有最高的成本收益比?如果是监管医院,这个答案一般是私人贵族医院——如果中国有的话。如果是非监管,那肯定有问题。因为他不能贵族化,收入上不去,又要保证成本收益比,只有降低成本咯。而且医疗系统里面,降低成本普通人根本看不出来。不普通的人——不普通还需要自己找非监管医院么?同样,一些用户不希望被监管的医疗问题中,这个关键词应当也是非常贵的。例如生育,肾亏,等等。这个过程也是不可监管的,百度自己难道还逐个核查竞价排名的真实性?他又如何有权力做这个事情呢?
一家不在监管下的医疗机构,这个问题够严重了吧?但是百度有做什么非法的事情么?没有。从法律角度讲,任何人有权付费将某个信息在百度的排名变更。例如,我可以付费将布什是条狗的网页调整到最高——如果我对布什不爽的话。这个不触犯任何法律,除非你调整有悖法律的关键字。你不能说布什是条狗不是事实,因而不允许我调整排名。那么,百度调整这些有问题的医疗机构的网页,并不能说他触犯了任何一条的法律——从法理上讲是这样的。
通常来说,如果是普通机构,市场会自行调整。如果一个公司提供的信息是违背市场本意的,那么这个公司本身就会被市场淘汰。如果你天天提供广告给我们,我们应当一脚把你踢开。问题是,百度获得了足够的互联网资源,百度搜索是个太重要的东西了。因此他可以屏蔽对自己不利的消息。于是,即使百度有问题,大家也不会知道,直到上面的这幕出现。百度被另外一个媒体的老大——央视——点名,他屏蔽不掉了——总不能屏蔽央视吧?当然,他还是屏蔽了部分消息,并且留下了相当的尾巴。
google的核心哲学观点之一就是“不作恶”。简单来说,就是不因为外力——包括广告,赞助,等等——人工改变排名。google的排名一般有两种变更方法,一种是被发现作弊或者犯规,另一种是更改算法。用google的话来说,即使我们认为某个关键字结果是错误的,修正错误的方法不是我们调整这个页面的pagerank,而是使用更公正的算法,保证每个人在同一个起跑线上。这个和美国法律的精髓如出一辙。即使我认为这个判例是错的,我也不会行政干预这个判决。而是通过议会修正法案来修正法律,保证一个更公正的法律。
至于google的广告,不要误会,google也是卖广告的。google的广告都统一显示在页面的右边,和左边的搜索结果严格分离。大家可以很容易的识别出google的广告。如果你们对广告内容有兴趣,可以点击广告——这是google广告的本意。如果你们对广告内容没兴趣,不强迫你们。这个是“不作恶”的本意。

2008年11月12日星期三

关于乙肝的一点常识

对于乙肝,贝壳自认为自己了解的已经够多了。至少贝壳知道两对半的意义,作用机理,还有一些乙肝的常识。不过在看了一篇文后,贝壳发现,还是不够多。具体内容可以看这里[1],中国内可能需要穿墙。
认识贝壳的都知道,贝壳是一个偏执于知识和真理的人。然而知识是否一定带来真理?是,也不是。知识未必带来真理,愚昧一定带来恐慌。上文中描述的乙肝患者歧视现象,贝壳并不怀疑。医院里面长长的体检队伍,电视上大量的乙肝药物广告,都是这一现象的残忍注脚。更不提贝壳从事的职业和传播学也有一定关系,自然知道资本和传播结合又没有管制的后果。那么今天,贝壳就着重提出几个乙肝的基础知识,看看大家是否了解。
乙肝是否会终身感染?
根据香港一个资料[2],幼年时感染后会终身感染,成年后感染基本会痊愈。
乙肝感染的方式和概率?
根据这个资料[3],体液交换会传染,包括献血,血液交换,性交和接吻。但是根据上面的文章[1],接吻传染的概率很低。
乙肝对正常人的传染?
根据资料[3]的说法,只要不发生血液污染,即使是夫妻这样亲密常接触的人,只要接种疫苗就可以防护。同时根据上一个问题,多数情况下你没感觉呢就痊愈了。作为80后的城市青年,贝壳记忆中从初中开始接种过三次乙肝疫苗,应当是终身免疫。
乙肝的后果和概率?
根据文档[1]的说法,也许运气不好的话(大三阳伴随谷丙/谷草异常)会肝功能受损(总体的20%),严重的引发肝硬化(受损的4%,总体的0.8%),少量的会形成肝癌(受损的0.4%,总体的0.08%)。按照当前中国全部乙肝患者全为大三阳肝功异常计算,会有96K人患上肝癌。如果考虑实际情况,大概会有1W人上下吧。
——如果您觉得很多,查查死于心脏病和高血压的人数,再想想您今天的午餐。
乙肝歧视的后果?
计划生育的后果是多出来的男性可以组建一支军队,而乙肝歧视的后果就是患病,无工作的1.2亿人口。——想想这帮人急了拿个针头在你家楼下扎人。
参考:
[1].http://item.feedsky.com/~feedsky/my1510/~5935684/129964642/1488578/1/item.html
[2].http://www.hku.hk/uhs/he/hep/chi-hepc.html
[3].http://www.hbver.com/Article/ygfz/ygzs/200511/4413.html

2008年11月9日星期日

SCIP,lambda,Church

贝壳最近在看SCIP,感觉受益匪浅。其中有一个2.6,使用函数表达数字,很难理解。贝壳查了查资料,这篇(http://blogs.sun.com /yongsun/entry/lambda%E6%BC%94%E7%AE%97%E4%B8%8Echurch%E8%AE%A1%E6%95 %B0)写的很好,贝壳就不多说了。贝壳把自己写的内容贴上来,作为一个借鉴。
(define zero (lambda (f) (lambda (x) x)))
(define one (lambda (f) (lambda (x) (f x))))
(define two (lambda (f) (lambda (x) (f (f x)))))
(define three (lambda (f) (lambda (x) (f (f (f x))))))
(define (add-1 n)
(lambda (f) (lambda (x) (f ((n f) x)))))
(define (add m n)
(lambda (f)
(lambda (x) ((m f) ((n f) x)))))
(define (mult m n)
(lambda (f) (m (n f))))
(define (show-func-number n)
(define (inc x)
(+ x 1)
)
((n inc) 0)
)
(show-func-number zero)
(show-func-number one)
(show-func-number (add-1 one))
(show-func-number (add one two))
(show-func-number (mult two three))
结果:
0
1
2
3
6
show-func-number这个函数是将高阶抽象函数序列映射到一个具体的数上的。工作方法是,建立一个函数x=x+1,然后使用给定的高阶函数来映射这个函数。n次高阶函数会映射这个函数n次,于是结果函数就是x=x+n。然后将这个函数作用于0,不难得到结果吧?

2008年11月2日星期日

一些关于盗版、黑屏、开源的事情

大家都知道,微软搞黑屏了。贝壳暂时就这个事情不发表评论,而是先说一些其他的事情,然后大家再回过头来看这个事情怎么说。
首先是软件的版权区别。开源软件,自由软件,免费软件,共享软件,收费软件,盗版软件,这些我们经常说的名词究竟有什么意义,有什么相同和区别?
首先,大家要了解一个事情,上述对软件的不同称呼,其实是不可并列称呼的。免费收费,是指软件的付费方式,开源闭源,是指源码的公布方式,正版盗版,是指是否侵犯版权。这些其实是不同的事情,只是很多事情有前后的因果关系,因此大家容易混为一谈。一般我们可以将软件分为是否收费,是否开源,什么版权三种分类方式。分清其中的区别有益于阅读下面的内容。
开源软件是指源代码开放的软件系统。多数情况下,开源意味着免费和自由,但是也存在收费的例子。例如许多大型系统(好像有些UNIX就是,但现在具体什么情况,贝壳没有用过,也没有看过软件协议),其源码对使用者开放(注意,开源并不代表对所有人开放,只要使用者有权获得源码即可。当然,如果范围缩小到使用者中的特定群体有权,则不算开源,例如微软的不可泄露协议),但是属于绝对的收费系统。大家很容易理解这里面的原因,既然源码已经开放,那么多数人都可以轻易写出类似的系统,在这种情况下还要坚持收费就愚蠢了。除非源码庞大,需要相当的水准和时间来理解,这样才能保持收费。当然,更多的情况是开源免费,收取专家服务费。
这里中间还要插入一句法律问题(怎么感觉写成法律普及文了),目标软件的作用是给予使用,源码的作用是表达思想,这是公认一致的原则。换言之,如果你发布的是病毒目标,则是违法。如果你发布了病毒源码(当然,要排除恶意发布),则是研究之用,不属于违法。当年DeCSS的审判之所以被判定无罪,即是基于上述原则。
免费软件是指授权方式是不要钱的。现在免费软件的很大一个来源是来自开源社区,然而并非只有开源了才免费,共享软件和试用软件就是其中的两个典型。共享软件的作者允许你可以免费的使用它的软件,但是并不开源。试用软件的作者允许你在一定期限内免费使用软件或其中的一定功能(其实试用软件的完整授权也不一定要用钱,写个邮件把作者夸一顿或者给他做些事情,例如翻译软件,一样可以获得授权)。这些软件虽然免费,但是往往会因为有其他的原因而选择闭源。例如微软的Process Explorer,就是属于共享软件的典型。这个软件原属于sysinternels的作品,后被微软收购。如果是开源软件,搞不好要和微软打官司,也不可能被收购。而Winrar则是试用软件的典型,大家都听说过Winrar推动检查中国大型公司内使用非授权产品的例子吧。这个例子难就难在取证这个软件产品超过了使用期限,因为大多数人可以通过重装来避免提示。
自由软件是一个非常复杂的概念,要理解需要了解一些西方法律精神。自由软件现在在中国基本被视同为开源软件,其实两者是完全不一样的两个东西。自由指的是你拥有软件的选择权,包括是否使用,是否修改,是否散发,是否改善,具体可以参考这个文档(http://www.gnu.org/philosophy /free-sw.zh-cn.html)。为了保证以上权力,开源是必须的,然而开源并不代表你拥有以上权力。我们在上文提到过,是否开源和什么版权是两个事情。开源软件可以选择收费版权,也可以选择非收费版权,但是禁止你修改,再散发软件。这些都不属于自由软件的范畴。
自由软件的起因来自于上世纪70年代出现在美国的自由潮。受到自由潮的影响,当时很多软件大牛都是黑客精神(不是现在这堆脚本小子讲的黑客)的拥护者。他们认为人类学习和使用软件的自由不言自明,他们拒绝为他们的帐户加上密钥,并且以破解软件系统为乐。他们所写的程序也是免费分发。很难想象,在上世纪70 年代的时候,很多现在具备极大影响力的项目在当时只是几个人看不爽而随手做的一些小程序。很多自由项目直到现在还无人可以超越,发挥着重要作用。
自由软件运动是天赋人权观念在知识领域的延伸,目的是推动知识的扩散。因为知识产品都有一个学习的概念,新手需要不断的观摩和学习成熟的系统才能成长。然而如果允许其他人无限制的学习,那么新知识的发明就无法给创造者带来利益,从而导致没有人愿意发明创新。因此专利法规定专利的存在,给予了发明人一定时期的权限,使其可以从中获利。而同时规定了专利期限,使得新手可以学习。(贝壳注:现在的很多专利期限动辄50年70年,实在是太长了一点,10年到20 年的期限应当是合适的)而自由软件在创造伊始就放弃了自身的专利权,给予了其他人学习和改进的权利,因此被认为是软件业的第一推动力。尤其是近些年,在 GNU的推动下,出现很多很优秀的软件产品。当然,其中大部分是和普通人无缘的。例如flex分析器,emacs编辑器。
盗版软件这个词很不好界定,因为有两种界定线。一种是收费软件不付费使用,一种是违反软件使用授权。从范围上说,后者比前者更广泛,因为付费主要是取得软件使用授权,不付费一定违反了授权原则。而违反授权则不一定是不付费,也可能是试用软件超期(违反试用授权中期限限定),未授权可以修改而进行修改(这个尤其多出现在使用源码库的时候),违反最终用户协定(在共享软件中常见)。一般我们说的时候都指前者,但实质上,后者也属于软件权违法的例子。我们不妨用违法软件来称呼后者,而用盗版软件来称呼前者。
盗版软件是否是自由软件思想影响下的产物?绝对不是。我们上文说了,自由软件运动的主要目的是普及软件知识,那么破解软件成果如何普及软件知识呢?无法自圆其说。也有人说这个是打击收费软件,以扩大开源软件的影响力。这就要讲到西方的毒树毒果理论,这个理论认为,非法手段(毒树),无论为了什么目地,其产生的结果一定是恶意的(毒果)。开源软件有着自己的适用范围,不需要也不可以通过这种方式强行介入收费领域。再者说,如果没有收费软件来为大型项目提供资金,没有大型公司来消化软件人才,那么程序员的将来也就无法保证,更谈不上进一步普及和推进计算机研究发展了。
盗版软件只是一些不喜欢付费或者根本不拿版权当回事情的人,为了自己的利益编造出来的一堆谎言。例如微软的这次黑屏,很多人都在抵制,都在骂微软。我们可以想象一下,如果微软的产品出来的时候就带着黑屏措施呢?他们照用不误,最多就是搞一下破解。Winrar也带了保护措施,用的人照样一堆堆,破解照样满天飞。微软只和合法购买者订立了合同,保证不会侵犯他们的权益。非法使用者从根本上就没有依据来保障,你的系统即使上了Windows就当场机器爆炸,也无法控告人家。
其实本质上说,贝壳也是违法软件使用者。在这个社会里面,看清每个软件的版权,然后一点不差的照做是完全不可能的,可能的只有知道行为违法后想法弥补。使用盗版windows则是因为贝壳根本是linux用户,但是同事全是清一色的windows,沟通不方便而被迫使用。既然我不是主动高兴买的,就上个盗版得了,被发现最多回到linux下结束(中国的法律对个人侵权行为只纠正行为)。使用盗版windows,我们人人知道违法,但中国的法律基于告诉乃论,就是所谓的民不告,官不纠。自己知道怎么回事,回去闷声发大财就算了,明明是违法者,还跳出来义正词严的指责受害者,做人不能太CNN。
就如同我在MSN名字中写的那样。我虽然不赞成你黑屏,但是我捍卫你黑屏的权力。

2008年10月29日星期三

封杀华硕宣告

兹因华硕陷害门事件(可于google上搜索 华硕 陷害门 黄静,不要使用百度),决定于今日起封杀华硕系列所有产品。不购买,不使用,不推荐,并向认识的人宣告此问题。特此声明。
P.S:虽然华硕不把我们当回事情,但是我们还是要把华硕当回事情,你想当下一个黄静么?

2008年10月20日星期一

程序生产流程管理的一些想法

程序的生产管理本质上说应当可以纳入生产管理中,然而程序毕竟是一种特殊的产品,因此程序的生产管理也有其特殊性。下面贝壳从小到大阐述一下个人对生产管理的一些想法。当然,30人以上的团队规模贝壳根本没有碰到过,因此就不予置评。
首先我们从软件的生产流程开始论述,当然你可以没有流程,然而你不能没有过程。没有流程叫做不正规,低成本和高效率,没有过程……没有过程我也不知道你怎么做的,直觉?
管理的头一步是计划,软件生产的头一步是调研和需求分析,然后是紧密关联的系统构架,包括构架选择和结构设计。调研的典型情况是回答以下问题,软件为了谁而做(Who),设计周期和维护周期是多长(When),软件的适用范围(When),软件的意义和核心价值(Why),软件要达成什么目的 (What),如何设计达成这些目的(How)。这些问题中,要达成的目的和如何达成是核心。而后通过详细的讨论,得出到底要做什么东西,具备什么功能,以及一些细节问题。这时期形成的是软件需求分析报告和软件需求说明书。需求说明书的尺度是一个很难把握的问题,一般来说,需要灵活对应市场反馈的软件,需求说明书不用过早细化,反之则可以早细化一些。开发人员多,结构复杂的系统,设计说明书必须详尽,反之则可以简单一点。不过如果忽视甚至无视需求说明书,或者将需求说明书仅仅作为一个官样手续的团队,必定会在后面吃大苦头。如果要规避需求说明书,也并非不可以。贝壳会在最后描述一个原型系统方法,来规避灵活和不确定的需求对需求说明书的挑战。然而注意,这只是需求说明书的形成手段,本质上是以程序员和最终用户的互动来清晰需求,同时潜在的让程序员熟悉需求。并非真的不用讨论和细化需求,更不是提倡做项目不出需求说明。
项目的构架选择是在基本明确需求后做的事情,这个阶段主要明确以下问题。是单机软件还是群体软件,C/S还是B/S,.net还是java还是 C,Windows还是Linux。中间是否使用ORM,使用的话怎么设计细粒度表,不使用的话怎么使用冗余设计。结构设计和构架选择互相分离又紧密结合,结构理论上是脱离构架的,然而某些结构就必须适用某些构架(例如如果用了事务明显就不能用mysql,性能会差死),某些构架则要求你特殊设计结构。结构设计是系统一个非常广阔专业而复杂的问题,有兴趣的可以看系统结构和构架的一些书,还有设计模式和UML的,这里就不细说了。
在系统完成结构设计后,就开始系统的编码过程了。而项目时间确定,到这个时候才有依据。粗说是编码过程,其实又细分为构架实现,技术研发,编码,自测试,系统整合几个部分。结构设计完成后,抽象的结构必须经过严格定义才能进行使用,其中进行严格定义并且文档化的过程绝对不要轻率处理。如果是大型团队,应当指定一个人负责结构定义的维护,并指定几个技术骨干来讨论定义,讨论修改。这个人需要处理每一个对结构修改的请求,分辨是否应当修改结构,并且交给骨干团队讨论。讨论确定后,对定义进行修改,修改定义文档,并且通知所有部门进行修改。
当结构定义后,系统的框架模型已经清晰可见了,剩下的就是编码了。可是否能进行顺利编码呢?未必可行。因为实际上会在过程中碰到很多技术问题。例如需要使用以前没有接触过的框架和组件,需要对抽象数学模型提出可行算法等等。这些问题如果不先行解决,后面的编码无法顺利进行,技术骨干的最主要作用就在这里。在他们解决技术问题后(或者,更经常的,在中间提出的问题被他们解决后),系统就进入了编码阶段。编码阶段的代码一般来说会有不同级别的自测试,从最简单的写个小程序到最复杂的单元测试+冒烟测试,按照项目的级别具体分析。不过即使是再小的项目再小的模块,一般写一点代码就写个小片断测试下是否可行是最基本的常识,除非你能保证所有程序不论大小一次写对。在完成自测试后,还需要整合入系统,并可能伴随冒烟测试。如果结构设计良好,这个阶段会非常顺利,甚至不出现什么大的问题。反之,如果结构散乱,不用到客户那里,在这步就会碰到非常大的阻力。
在完成主程序的编码和整合后,项目进入收尾阶段。一般是漫长的测试和后续工作,主要包括叠代测试,性能分析,编写使用手册和编码手册,编写项目的技术分析,系统分析报告和各种材料。在这个阶段最主要是要通过测试,先于客户找出错误,并且逐步修改掉。良好的测试结果应当是逐步收敛的,当你看到一个逐步发散或者不稳定,根本没有规律的测试修改结果时,你的麻烦就大了。这通常是因为没有构架,构架错误,中间有不适应构架的修改,构架变化,核心算法错误,需求浮动太大等根本问题所导致的。当然,在测试的同时还要进行全面的文档化过程。
在测试和交付后项目是否结束呢?恐怕还没有。下面是漫长的客户服务期,需要收集和分析客户反馈,进行持续改进。不过那就是后面的问题了。下面我们按照团队的大小来逐步讨论团队的分配和任务。
首先是从一人团队开始,当然,如果也能叫团队的话。作为一人团队,也就没有什么分工问题。文档化要以轻量为主,方便自己日后理解,除非是客户特别需求。
而后是典型的一个团队,一个PM带几个技术,可能还有外面的美工支援什么的。人数不超过五人,不分组。这里前期的需求/后期文档都要由PM完成,程序员主要注重编码和测试(尤其是单元测试)。如果时间充裕,建议一些专门编码一些专门测试,这样可以有效保证代码质量。互相的沟通以开会为主,信息的沟通要诀是让每个人都知道别人的事情,尽量多的向别人传递信息。
再下面是一个典型的“大”团队,两个PM带四个程序员四个测试,其中有两个以上技术骨干,再加上一个专职美工和专职的UI Design(或者两个美工,基本差不多),8-16人的“大型”团队。说大型是因为这个团队开始内部就要分工协作了,加引号是因为……即使10个人,基本也就刚够分工的底线而已。
贝壳个人建议,除开美工等支持岗位,将这种团队分成三个部分。一个是PM组,负责和用户沟通协调,产生需求文档,盯项目进度,调度程序员,产生用户文档,产生其他材料。这组PM不要求高技术,对于技术建议会用,但不用精通(最好也别精通,业务骨干做PM是非常浪费的)。但是对于沟通技巧,协调能力和领导能力要求非常高,也要求有相当的文字功底。毕竟他们是要和客户沟通的人,要是鸡同鸭讲就麻烦了。和客户产生矛盾,文档写不好,更是麻烦中的大麻烦。由于协调要求非常高,因此PM组强烈建议至少两人,手机24小时开机!建议设立正副职,采取正职负责,副职挂钩的方式。
第二个团队是研发组(Dev),至少一个技术骨干带队。这组需要负责编码,自测试,系统整合,出开发文档,出技术文档。对于他们要求是沟通能力过关,程序编码效率高。对于系统经验,思考方式的全面和独特没有特殊要求。一般经过培训的新程序员就可以在研发组中担任工作。他们年轻力壮精力旺盛,相对编码效率比较高。不过如果有条件,还是用比较有经验的程序员比较好。在系统的整合测试,返工引发的效率低下控制方面会有相当的好处。
第三个团队是测试组(Test),至少一个技术骨干带队。这组要求负责系统的叠代测试和性能测试,可能还要帮助编写用户文档,进行项目实施,培训和售后支持。这组人的要求是工作勤奋(叠代测试的工作量是非常高的),技术过关(否则无法发现一些问题),系统经验丰富,思考问题全面而独特。因此强列建议由最强的技术骨干和一帮能吃苦的年轻人组成。一般来说测试组和研发组的人员比例在1:2到1:1之间。如果小于1:2,那么会发生测试不充分的情况。如果大于 1:1,只要成本允许,到是强烈支持。
最后一个团队(有人算了算……怎么还有),是系统的管理组。负责项目的构架选择,结构设计,时间节点认定,人事事务,开发成本控制,技术研发。这个团队有非常高的技术能力,管理能力和执行权限,一般由主PM,研发组和测试组骨干,客户代表,公司代表(多数和主PM是一个人)组成。主要是要对项目过程的监控,项目中人员权限的分配,核心技术的研究和管理进行处理。这个团队等若公司和客户的联合代表,对项目负全部责任。
对于30人以下的团队,估计可以按照比例放大组规模来使用同样的组织结构。不过如果再大,同样结构就不合适了。这主要是因为同一个组中的信息是互相完全流通的,超过10人的组会造成非常高的沟通成本。这时候一般是分解系统结构,分解研发组和测试组。将一个大型系统分解为两个或者多个独立的部分,让每个组分别研发和测试。这样可以避免每组内的信息沟通成本过高,可对文档的严密性和规范性提出了更高要求。通常来说,拆分方法有按照功能和按照构架。即按照功能划分出一个一个的业务模块,每个组开发一个完整的业务模块。和按照构架层次分为数据库组,业务逻辑层组和客户层组。通常来说,我支持以业务为主的拆分方法。因为此时组已经够大,让一个组精通所有层次不难。但是让所有组全部完整了解需求可就很难了。
对于这种成规模的开发,注重的主要是两点,文档和测试。最高的要求(也是我认为最好的褒奖)是及时的文档和全面的测试。此时的难点在于,研发过程中程序员经常为了修补问题而修改代码,但是忘记修改程序文档。或者需求变更后PM改了需求说明,通知了Dev,但是忘记通知Test。又或者通知了Test,但是忘记修改用户手册。因为诸多文档其实是对同一内容的不同描述,所以相互具有关联性。其中之一变化经常导致其他文档落后陈旧,而且版本不统一。
测试应当贯穿正规研发过程。从程序员实现一个个功能起就应当开始叠代,直到项目完成后。并且bug的管理应当和需求管理合并,成为几个组沟通的核心。测试的时候一定要注意充分测试和叠代测试,不要象微软一样弄出新的补丁补出老Bug的状况。
下面贝壳讲以下系统原型法,其实这个就是业界常说的敏捷开发。系统原型方法是指以构建非常简单的系统,实现非常核心功能的原型系统为基础,逐步推导出正规系统的功能和需求的系统分析方法。主要适用于系统需求不清晰,分析困难,开发周期短,程序员数量适中的项目。如果上述条件不成立,那么建议不要使用原型方法。
原型法的头一步是分析需求,不用说不确定的,就说为了实现业务目的(至少这个应该知道吧?)需要哪些功能。然后实现一个可用的,不用很美观,不用性能优化的系统。有了这个系统后,客户可以逐步分析使用这个系统哪里不方便,而后交给程序员改进。逐步反复,直到客户满意为止。使用这个方法,客户和程序员间,程序员互相之间要保证充分沟通,文档可以容后再写(前期还不确定呢,怎么写?)。主要是注意逐个的需求管理和Bug管理,这正好和我上面说的合并管理对应。使用这个方法的好处是当不清楚需求的时候可以马上做,逐步清晰。过程比较直观,做出来东西比较实用,也节约时间。坏处就是会浪费一定的程序员人力,而且一个没控制好就一直改一直改不知道哪里是头了……
当前国内软件业企业的几个问题就是,不注重需求,不注重测试,不尊重专业,不尊重规范,不培养人才,不积累技术,不重视信誉,不打算做事。下面贝壳逐个细说。
不尊重需求,一般来说,老板讲的时候都是需求为重的,可当客户需要变的时候,老板很容易同意需求变更(虽然我可以理解,做生意也不容易)。不尊重测试,做程序的非常理解测试的重要性,然而老板却认为那个岗位可以随便找个人来做。实际上,测试是一个非常专业非常流程化非常严密的东西,测试的主管最好是公司里面最有经验的人。同时,还有不尊重专业的问题。并不是说老板干预程序员的决策,而是很多时候老板根本不了解技术骨干和PM,测试的区别。让技术骨干来做策划,或是负责、或者主导和客户沟通,这都是超级缺乏效率的做法。至于不尊重规范,事先划定的流程,在遇到重大问题的时候,往往就变成了废纸一张。到不是说在重大问题前非要坚持僵硬的步伐,可一个项目一半时间都是重大问题,这就过分了把?先说了项目过程中要推进知识积累,推进技术交流,推进这个推进那个,等项目一忙就全飞了。
同时由于程序员的超高流动率,当前中国的程序界有一个非常不良好的风气,公司基本不培养自己的程序员。都不知道公司是否能开到明年呢,培养了做什么呢?这点在大型公司就比较好,无论什么情况,基础的内部交流总是保证的。只要签长约,多数可以弄到一些培训。中小公司不培养人才一方面是没有必要,另外一方面就是没有能力。于是程序员就被迫自我培训,自学或者脱产参加培训。付出了成本,自然要赶快跳到能实现价值(能把钱赚回来)的公司里面去。中小公司为人员流动付出巨额成本,而且很多都根本无知觉。
举贝壳公司的例子吧。因为发展需要,今年年初公司曾大型招人,C#程序员,结果可用者寥寥无几,很多都是浮夸碰运气的。以至于一天面试十多个人,竟然一个备选都没有的情况经常发生。一个人过来投简历,硕士,要价10K多。不说公司能否负担,看了看做的题目,算法题还不错,C#技术,解决实际问题都一塌糊涂。这种人招进来差不多就是研究算法写Paper的主,要做程序还得培训一下。还有一个人,我前周刚刚送走,转眼又回来,估计是批量投简历的时候忘记筛公司了。还有一个项目经理真的是不错,讲起问题来很深入,经验丰富,可老板认为用不到,贝壳一点办法都没有。由于人员仓促到位,我们在开发后期付出惨烈代价!有一个程序员从到岗到离开公司,最大的贡献就是拖了三个多月的进度,因为他连static函数干吗用的都不知道。还有一些人,很适应岗位,可做不到多久就走了(当然,这是有各种原因的,试用和刚满期的人走是比较正常的事情)。问题是,其他人就要重新接手他的事情,等来了人再换手。这样的直接结果是什么呢?如果说项目拖延有一半是因为我们需求没做到位,另外一半就是团队的人才损失。如果在普通项目上碰到类似问题,来的人不能做事情,人员替换率高。那么本来能按时完成的任务就一定会延后,而且分析的时候很难直接表现出来,多数会被认为是工作效率不够高,工作态度不认真之类的(某种意义上也没错,毕竟不能做事的人,不是因为效率不高就是因为做事不认真)。不能留住人才造成的后果,是通过项目拖延表现出来的,使得公司往往失去了隐性可能的良好口碑。这种软性杀伤是非常致命的但是又是难以直接表现的。
如果说不注重人才,还怎么能积累技术呢?人是技术最主要的载体。尽管我们可以通过互相培训,技术交流,技术文档化来积累技术。但是如果没有老员工的指点,那么新员工是很难吸收企业的原有技术体系的。无法积累技术的直接表现有两个,一个是中国企业没有核心技术,另外一个就是掌握技术的人就卡了公司的脖子。可能有人会举出中国有多少专利多少成果。贝壳告诉你,按照贝壳做项目的经验,那个多数都是项目做好了用来表功的牌坊。很多技术都是公司不敢给个人,个人不敢给公司,因为浮动率太高。许多真正有价值的核心技术往往是因为缺乏大公司(或者缺乏人)作为后台,而无法正式的走向商业化运作,更无法走向系统化理论化。因此中国不但缺乏真正的核心技术(我指能解决问题,有实现难度的技术),更缺乏(这点可以确认)系统化理论化的技术体系。而掌握公司核心技术的人往往就能卡公司的脖子,尤其是技术都掌握在一个人手中的时候。并非说程序员都有坏心或者什么的,而是程序员有很多和老板不一样的想法(例如要重视测试,要重视专业等等)。当程序员觉得他是对的时候,为了和老板争辩,往往会使出走人的杀手锏。固然,公司是对产品拥有产权的。可是掌握核心技术的人不在,没有人能继续改进,研发新的产品系列,这不是要公司的命么?这时候老板就处于弱势的一方。从某个事情来说程序员往往是对的,可是从企业发展来说却绝非好事。
最后两个问题则是中国软件业更深层次的问题,不重视信誉,不打算做事。整天就想着前辈一夜暴富的事情,或者谁谁风投成功吃喝不愁的事情。根本不打算花心思将事业做好,而是设法请客招待人拉风投,找人做假买点击量买排名,花钱黑掉对手的网站,盘剥底层员工,炒作一些无聊的事情增加知名度。某种意义上说,这个才是中国软件业最大的毒瘤。

2008年10月12日星期日

一件最XXX的事

好像小时候老师经常出这样的题目,不过我每次都没得可写,总觉得那堆事情太假太做作。没想到,我这两天亲身经历了一件很囧的事情。
事情是这个样子的。贝壳前两天玩开心(好啦,我知道很无聊,不过发现一堆N久没有联系的同学,很好玩那),然后突然想到搜索高中同学。首先跳出来的是我们的于飞同学,据记载,不出我们所料,去日本了。然后是一位叫江宁的同学,哪位?贝壳怕自己忘记了某些曾经的同学,所以打算点进去看看她的好友里面是否有高中的朋友,然后发现——这是谁?
她的列表中有一位“阿达”,这位同志刚刚加我,并且和我一样,是赵一搏的朋友,换句话说,是初中和大学圈子里面的。是不是贝壳点错了?还是开心太强大了?
事实证明,都不是,是事情太巧合了。贝壳联系该同志本人后发现,所谓“阿达”,乃是丁之光同学,正宗的初中和大学同学。而江宁同学在牛栏山高中复读,是杨亮和大佟的同学。他们是同事,我发消息的时候正好在旁边,OMG。
够囧了吧?好戏还在后面呢——
贝壳一时高兴,问,那个秦伯韬你认得么?认得。段旭辉呢?认得。张雷?认得。王巍?认得。朱金辉?认得。许智翔?谁?
shit,怎么就是不认得贝壳,俺这么有名的说。
不过此同学认出了我一箩筐的高中同学,于是贝壳自己都混了。然后说——段是在上海念书么?不是。那上海念书的是谁?张雷。(到底谁在牛山混的久啊?)哦——我知道,大佟的男友吧——
刚刚说完——对面发出了一阵吓死人的叫声——
(贝壳):说错了么?我记得是杨亮女朋友和我说的(不就是刘莹同学么)——
(江宁):张雷很帅的——
(贝壳):小声点,大佟也是你朋友——
(江宁):呃——
(贝壳):好像我记得一起看过他们,那次我去复旦玩,大佟和张雷都来的,朱金辉陪他四中的女友,没来——
然后对面发出一阵杀气——
(江宁):说,朱在复旦有几个女友——
(贝壳):啊,我又说错什么了?
(江宁):我一个姐们,是朱前女友——
(贝壳):(原来是上门讨债的,少说的好)哦,哦,可能我记错了——
(江宁):OOXX..**(省略一堆话,具体可以自行想象),不过你是可能记错了,当时王巍的女朋友是四中的——
(贝壳):。。。(乌鸦飞过)。。。——
天啊,贝壳不想活了——

2008年10月11日星期六

分词算法的具体实践

说到分词算法,可能很多人都很陌生,然而说起百度,google,很多人却是耳熟能详。google,百度在搜索的时候,输入关键词后瞬间就可以得到结果,如果用通用数据库是无法做到的。实行这个加速的关键就是分词算法。例如"项羽是萝莉控"这句句子,我们一般搜索都是搜索项羽,或者萝莉控,萝莉。你见过有去搜"是萝"这个关键字的么?因此系统通过分词,将句子分解为"项羽/是/萝莉控",去处单字常见词"是"(如果要索引"是",可以想像有多少文章没有"是"的),我们就得到了项羽和萝莉控两个词。再通过反向关联,建立项羽,萝莉控指向文章的连接,就可以完成瞬间的搜索了(具体原理不说了,只要有一定数据库基础的人都应当能想明白原理)。并且通过关联性,某种程度上也可以提供"是萝"的搜索(带"是"的词,带"萝"的词,相关度最高)。
那么,如何来计算分词呢?方法很多,大家可以在网络上搜索下,贝壳就不赘述了。贝壳现在要说的是这次贝壳主要试验的方向,基于词典的机械分词中的最大分词算法。
机械分词算法是当今的主流,关键原因在于速度问题。虽然正确的分词很有价值,然而如果速度太慢,一样没有什么用处。机械分词一般可以保证 98%-99.5%以上的正确率,同时提供极高的分词速度。而机械分词一般来说,都是基于词典的。主要有正向分词算法,逆向分词算法,最大匹配分词算法。其中最大匹配分词算法具备最高的灵活性,只要你能评价一个切分的优秀程度,算法能把所有可能算出来让你评价。然而大家可以想像,这个是非常耗费CPU的。贝壳在这个基础上,做了一个具体的实现和细化加速。并且有准备做为一个开源项目来长期运作(只要有人有意向接手合作)。
首先我们先说贝壳这个算法的评价原则。贝壳认为,评价原则应当有以下几点。同时也必须要说明,以下原则是无法正确评价所有情况的。不过以下原则在原则正确的基础上比较便于优化。一、无法分析的词最少(这是全局最大匹配的理论核心)。二、匹配出的原子最少(这是保证分词优秀性的指标)。三、匹配出原子的出现概率和最高(这是纯粹没有办法了从概率上提高匹配正确的可能)。
当我们分析一句话的时候,我们可以想像,这句话应当是正常的,可被理解的。换句话说,句子中应当都是有意义的词。那么,在匹配后无法理解的词是什么呢?一种是匹配错误,一种是新单词,一种是单字成词和无意义助词。单字成词的例子有上面的"是",我们可以通过一个比较小的词典去除。那么,假定词典够大的情况下,无法理解和分析的词越少的组合越正确。而同样一句话,匹配出的原子越少,在搜索的时候效率越高。因此我们有规定了原子最少原则。至于最后一个,在无法分析词一致,原子个数一致的情况下,我们只能通过出现概率来猜测可能性。
然后,现在让我们分析一下分词的特点,并且做一定的优化。首先就从最著名的例子,"长春/市长/春节/致辞"开始。
>>>长春市长春节致辞
首先,匹配算法一定要先搜索到一个出现的词,有词才有匹配优化问题。没有词的话,你试试看分词"嗡嘛呢呗咪吽"。根本无法可分。因此首先我们要计算出一个出现的单词。贝壳是从正向开始计算的(主要是因为词典的加速方法是头索引的)。
>>>*长春*{市长春节致辞}
>>>*长春市*{长春节致辞}
好的,我们匹配到了两个,不过这就是全部可能么?不是,否则就变成了正向最大搜索。你可以看看"有意见分歧"。如果从头一个匹配到开始计算,无论如何都是"有意/见/分歧",而事实是"有/意见/分歧"。因此我们还有一种可能,在头一个匹配到的位置,其实并不匹配。不匹配多长呢?最大长度不会超过最短的匹配词。为什么?我们来看下面一个例子。
>>>*长春*{市长春节致辞}
>>>*长/春/(这两个字不是词,而是两个无法理解的字){市长春节致辞}
很明显,后一种分法违背了我们的第一原则,无法分析的词最少。无论后面怎么计算,其最优结果是相同的。在后续结果相同的情况下,头一次匹配到词后,所有可能的跳空(搜索可能的不匹配)最大长度严格小于最短匹配词的长度。
那么是否所有跳空都要搜索呢?也不,我们可以继续剪枝。对于情况"有意见分歧"来说,这个路径是必须搜索的。但是对于我们的例子来说,是无需搜索的。为什么呢?我们看以下计算。
>>>*长/{春市长春节致辞}(下一个匹配是什么?总不会是春市吧,所以应当是"市长")
>>>*长/春/市长*{春节致辞}
>>>*长春*{市长春节致辞}
大家可以看到,其实这个路径是无需计算的。那什么情况下需要计算呢?
一旦跳空,其跳空后寻找到的下个词的位置必须严格小于最短词的词尾位置。否则就没有搜索价值。具体可以看以下示例。
XXXXXXXNNNNNNNNNNN(X是词,N是无关紧要的)
SSSSSSSXXNNNNNNNNN(S是跳空或者跳空后形成的无法理解字,X是词,在这种情况下,无论后面怎么评价,都不影响该匹配被剔除)
OK,我们回到例子,刚刚我们说了,有"长"的匹配。但是通过刚刚的剪枝,又被剪了出去。我们下面分别计算两个情况。
>>>市长春节致辞
>>>*市/{长春节致辞}
>>>*市长*{春节致辞}
>>>长春节致辞
好,我们先不计算下去了。通过上面的计算,我们发现,在计算过程中经常需要计算同一内容的结果。我们可以想一下,同样的分词,同样的算法,出现的应当是同样的结果。就是说,分词函数是状态无关的算法。通过分解一个单词,得到一个最优结果。那么,我们对于同样的数据,何必需要计算两次呢?贝壳上文中提到过记忆函数,这次就用上了。根据贝壳的试验结果,如果记忆全部词的分解结果,会造成大量的记忆-释放,而内容基本没有用到,造成效率下降。如果只记忆长词的分解结果,往往又会因为太长,大多数句子无法达到长度而根本没用。这中间有个平衡值,具体多少贝壳就不说了。我们可以按照上文的方法计算以下两个过程,得到结果。大家可以自行验证。
>>>春节致辞
>>>*春节*致辞*
>>>长春节致辞
>>>*长/春节*致辞*
>>>*长春*节/致辞*
结合上面的过程,我们推算得到结果。
>>>*长春*{市长春节致辞}
>>>*长春*市长*春节*致辞*
>>>*长春市*{长春节致辞}
>>>*长春市*长/春节*致辞*
>>>*长春市*长春*节/致辞*
按照上面的评价原则,我们得到了正确的结果。
大家可以看看其他例子,这里着重说一下"有意见分歧"。
>>>有意见分歧
>>>*有*意见*分歧*
>>>*有意*见/分歧*
注意,有是单字成词,见可不是。如果见单字成词,做看见讲,那这句话就彻底成歧义句了。可以理解为,有意的要看到(或者让其表现出)分歧。这一般是古文语法。由此也可以看出上述原则在理解古文的时候往往会出现问题。同时还要指出的是,在匹配"长春市长春药店"的时候,会出现以下结果。
>>>长春市长春药店
>>>*长春*市长*春药店*
>>>*长春市*长春*药店*
两者的无法理解词都没有,切分数一致,最后硬是因为春药店出现概率低而被筛掉。可见系统有的时候是依赖概率和人品在工作的。
经过上面的原则和算法,贝壳实现了一个python的分词程序,1000行代码,原型系统。90W条词情况下,在AMD MK36上(2G主频)分词效率66K/s上下,具体看分词的选项(例如顺序分词就比较节约资源,分词排除重复就比较慢,启用多线程后在单CPU 机器上更慢),内存使用114M。使用C++写的核心词典后,90W条词的情况下分词速度80K/s,比python的核心词典快了20%,内存70M,节约内存40%。不过可惜,这个核心词典是公司产权,贝壳无权公布。并且贝壳做了一些工作,准备使用分词程序来生成分词词表。这个么贝壳就不准备讲了。前面讲的内容贝壳准备放到试验型站点 http://shell909090.3322.org/split_word/split_show/上面去,08年内有效。有兴趣联系我的可以发 mail给我,shell909090@gmail.com,欢迎大家试验并提出意见。

2008年10月6日星期一

苏博婚礼回来暨python2.6发布

这次10.1算是个大日子,因为我们可爱的苏博终于和他美丽的新娘结婚了。据说两个人相识10年拍拖7年,找的高中班主任做证婚人。实在有点为难人家,到底说高中就好上了呢?还是高中没好上?不过总而言之,他们总算结婚了。具体苏博是怎么被我们蹂躏的,以及婚礼的起因经过什么的就不写了,毕竟我不是新闻记者。这次就写一些有趣的事情和感想。
首先是去震泽的车子,因为10.1的关系,并不怎么好去。不过坐在车上晃晃悠悠两个小时,看旁边的河跟路一起走,感觉还是很不错的。江南不愧是水乡,有条河就在我们的路旁跟了10多分钟,还有条船跟我们并排跑。震泽古镇也很灵的,宝塔街古香古色,保证没有现代元素,除了大头发现的几个公共厕所外。建议大家有空可以去看看,苏博的家乡。
而后是新郎和新娘的一个让我比较震撼的问题。婚礼上,主持人问新娘的大学同学,是否在校园里面经常看到新郎。人家说,一直以为苏於良是南大学生。我吓一跳,南大啊,我一直以为王苏瑾在上海念大学。由此我得到一个结论,远距离恋爱是否会失败,和双方爱对方的程度无关,而和双方把爱付诸行动的程度有关。其实不光远距离恋爱,婚姻也是一样。认识我的人都知道我的两个总结。夫妻双方性格相近或相反,价值观一致。今天看来还要加一条,愿意将爱付诸行动。
然后是婚礼前一天,阿丁同学打过来跟我哭诉她和她男友的情况。实话说,虽然被哭诉半天,但是我还是搞不清楚她和她男友的状态,总之是非常复杂一团浆糊。因为隐私关系,我不打算说她和她男友的具体状况。不过大致就是她很喜欢他男友,喜欢到没有自我没有尊严。他男友呢,则是有点——不知道怎么说。说有问题吧,说不出来,说没有问题吧,情况确实——不怎么好。而且她本人处理事情上也不是没有问题,我觉得这个应当叫孽缘吧。不过无论如何,我的建议是——分手。
然后我就建议阿丁同学到震泽来玩一天,反正黄禹同学正好没来。然后她跑来玩了一天,回去和我说了一句雷晕人的话。我彻底无语了——
无论如何,那是她的家事。
再后面就是苏州到上海的车,同样也不怎么好弄。我问今天又没有去上海的车,最好是动车。回答说有,动车。我说来两张票(帮人代买一张),售票员说,晚上11点半的哦~~
我彻底无语。
后面一个朋友则更悲惨。他问,今天到南京的车票还有么?没了。明天的呢?也没了。后天的呢?我们只发售今明两天的~~
最后我们坐大巴回来的。
最后的最后,说一下,python2.6发布了,虽然我不打算用。比以前在构架上有了不少进步,不过很多东西暂时没有这么快迁移过去。我打算等3.0出了后直接用3.0,反正程序是一样写的。

2008年9月24日星期三

VeryCD版电驴(eMule)存在封锁

eMule是一个GPL程序,所以VeryCD的改版必须公开源码。今天听说VeryCD版有封锁的现象,所以贝壳抓源码来看看。如果大家认为老调重弹的话,不妨把文章拉到最后。
源码从此处下载:http://www.emule.org.cn/download/
最下方链接:http://download.verycd.com/eMule-VeryCD-src.rar
贝壳下到的文件大小13,703,064字节,打包时间2008-09-11。经过贝壳查找,在eMule-VeryCD-src\src \WordFilter发现两个文件,WordFilter.cpp 2008-03-12 09:57 13374和WordFilter.h
2007-11-20 17:56 1009。仔细阅读里面,发现有以下内容。
void CWordFilter::Init()
{
HANDLE hFile;
DWORD dwRead;
int nLen;
BOOL bResult;
CStringList list;

//m_count = 0;

CString saaa = thePrefs.GetMuleDirectory(EMULE_EXECUTEABLEDIR) + FLITER_FILE;
CString sbbb = thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + FLITER_FILE;

// 如果文件目录不对,程序移动一下,到config目录下 added by kernel1983 2006.07.31
if (PathFileExists(thePrefs.GetMuleDirectory(EMULE_EXECUTEABLEDIR) + FLITER_FILE))
MoveFile(thePrefs.GetMuleDirectory(EMULE_EXECUTEABLEDIR) + FLITER_FILE, thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + FLITER_FILE);

if (!PathFileExists(thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + FLITER_FILE))
{
// 不存在,所有的都过滤 added by kernel1983 2006.08.08
m_filterall = true;
return;
}

// Open file for read
hFile = CreateFile(thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + FLITER_FILE, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
//AddLogLine(false,_T(":%s\n"),thePrefs.GetConfigDir() + FLITER_FILE);
if(hFile == NULL || hFile == INVALID_HANDLE_VALUE)
{
// 读取错误,所有的都过滤 added by kernel1983 2006.08.08
m_filterall = true;
return;
}

DWORD dwSize = GetFileSize(hFile, NULL);

TCHAR * pszData = new TCHAR[(dwSize / sizeof(TCHAR)) + 1]; // 申请空间
bResult = ReadFile(hFile, pszData, dwSize, &dwRead, NULL); // 读入文件1
CloseHandle(hFile);
pszData[(dwSize / sizeof(TCHAR))] = 0;

if(bResult)
{
// 加入解码算法
{
std::string tempstr( (char*)pszData + 1 , ((int)dwSize - 1) > 0 ? dwSize -1 : 0 );

// 查看是否是老格式
char * pszData_a = (char*) pszData;

if( pszData_a[0] != 0x15 ) {
// 老格式,进行转换
CUnicodeToMultiByte wc2mb( thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + FLITER_FILE );
tempstr.assign( (char*)pszData , dwSize );
InternalBase64::encode2file( tempstr , std::string((LPCSTR)wc2mb , wc2mb.GetLength()) );

delete [] pszData;
// 重新载入
return Init();
}

vector vec = InternalBase64::decode( tempstr );
char * pszt = (char*) pszData;
for( size_t i = 0; i < vec.size() ; i++ ) {
pszt[i] = vec[i];
}
dwSize = vec.size();
}

TCHAR * pszTemp = wcstok(pszData + 1, _T("\r\n"));
while(pszTemp != NULL)
{
nLen = wcslen(pszTemp);
while(pszTemp[nLen - 1] == '\t' || pszTemp[nLen - 1] == ' ')
{
nLen --;
pszTemp[nLen] = 0;
}
while(*pszTemp == '\t' || *pszTemp == ' ')
{
pszTemp ++;
nLen --;
}
//AddLogLine(false,_T("pszTemp:%s"),pszTemp);
//AddLogLine(false,_T("nLen:%d"),nLen);
if(nLen > 0)list.AddTail(pszTemp);
//if(nLen == 8)AddLogLine(false,_T(":%d %d %d %d "),((char*)pszTemp)[0],((char*)pszTemp)[1],((char*)pszTemp)[2],((char*)pszTemp)[3]);
pszTemp = wcstok(NULL, _T("\r\n"));
}
}

delete[] pszData;

m_count = list.GetCount();
//AddLogLine(false,_T("m_count:%d"),m_count);

if(bResult && m_count > 0)
{
m_filterwords = new TCHAR*[m_count+1];
m_kmpvalue = new int*[m_count+1];
ZeroMemory(m_filterwords, sizeof(TCHAR *) * m_count);
ZeroMemory(m_kmpvalue, sizeof(int *) * m_count);
}

for(int i = 0; bResult && (i < m_count); i ++)
{
CString s = list.GetAt(list.FindIndex(i));
s.MakeLower();
nLen = s.GetLength();
//AddLogLine(false,_T("nLen:%d"),nLen);
m_filterwords[i] = new TCHAR[nLen + 1];
m_filterwords[i][nLen] = 0; // 最后一个字节设为0
m_kmpvalue[i] = new int[nLen];
//AddLogLine(false,_T("nLen:%d"),nLen);
_tcscpy(m_filterwords[i],s);
//AddLogLine(false,_T("m_filterwords[i]:%s"),m_filterwords[i]);
KMP_GetNext(m_filterwords[i], m_kmpvalue[i]); // 得到一个与内容有关的数值m_kmpvalue[i]
}

if(m_count == 0 || !bResult)
{
Free();
//m_filterall = true;
}
}


bool CWordFilter::VerifyString(const CString & sString) // 验证字符是否合法
{
bool bIsRm = sString.Right(3)==_T(".rm");
CString sReduceString=sString;
CString sInterpunctionString = _T("(),().。·;:-《》『』~ “”〓!【】★×┇");
try // VC-Huby[2007-03-20]:满足中国国情特色,加强过滤
{
int j=0;
for( int i=0; i< sString.GetLength(); i++ )
{
if( sString.GetAt(i)<=_T('/') && sString.GetAt(i)>=_T(' ') ) //从空格到'/'之间的字符减掉后再过滤
{
continue;
}
else if( sString.GetAt(i)<=_T('@') && sString.GetAt(i)>=_T(':') )
{
continue;
}
else if( sString.GetAt(i)<=_T('`') && sString.GetAt(i)>=_T('[') )
{
continue;
}
else if( sString.GetAt(i)<=_T('~') && sString.GetAt(i)>=_T('{') )
{
continue;
}
else if( sInterpunctionString.Find(sString.GetAt(i))>=0 )
{
continue;
}
else
{
sReduceString.SetAt(j,sString.GetAt(i));
j++;
}
}
if( j< sString.GetLength() )
sReduceString.SetAt(j,_T('\0'));
}
catch (...)
{
}

if(m_filterall){
//AddLogLine(false,_T("m_filterall"));
return true; // 检测不到文件,或者读取错误的情况下放弃过滤
}
if(m_count == 0){
//AddLogLine(false,_T("m_count == 0"));
return true; // 文件是空的时候,放弃过滤功能
}
CString strSearch = ((CString)sReduceString).MakeLower();

//vc-huby: 过滤中文字符超过15字符
//CString sReduceString2=strSearch;
int k=0;
for( int i=0; i< strSearch.GetLength(); i++ )
{
if( strSearch.GetAt(i)<=_T('9') && strSearch.GetAt(i)>=_T('0') )
{
continue;
}
if( strSearch.GetAt(i)<=_T('z') && strSearch.GetAt(i)>=_T('a') )
{
continue;
}
else
{
k++;
}
}

if( k>=20 && bIsRm )
return false;
//int m = sReduceString2.GetLength();
/*
if( k>=60 )
return false;*/


/*if (strSearch.GetLength() > 20)
{
return false;
}*/

for(int i = 0; i < m_count; i ++)
{
if(KMP_Match(strSearch, m_filterwords[i], m_kmpvalue[i]))
{
//AddLogLine(false,_T("KMP_Match"));
return false; // 关键词命中了,被fliter了
}
}
//AddLogLine(false,_T("漏掉的"));
return true;

}

void CWordFilter::Free() //
{
for(int i = 0; i < m_count; i ++)
{
if(m_filterwords[i])
delete[] m_filterwords[i];
if(m_kmpvalue[i])
delete[] m_kmpvalue[i];
}
delete[] m_filterwords;
delete[] m_kmpvalue;
}

CWordFilter::~CWordFilter()
{
Free();
}
其中WordFilter.h的第17行有以下定义。
#define FLITER_FILE _T("wordfilter.txt")
于是贝壳查看了C:\Program Files\eMule\config目录,在下面发现了wordfilter.txt 2007-09-30 12:58 10788。大家有兴趣自己看看里面的内容,贝壳就不贴了,贴出来绝对被封,死1090次。
下面说一点起效方式,也许大家很奇怪,这些内容是可以搜索的。贝壳仔细查看了代码,类在两处被引用了,一个是MFC初始化系统的时候初始化类,载入词典。另外一个是在SearchList.cpp 2007-11-20 17:56 22505,351行AddToList函数,第360行,内容如下。
// WordFilter added by kernel1983 2006.07.31
if(!WordFilter.VerifyString(toadd->GetFileName()))
{
delete toadd;
return false;
}
这个封锁手法尤其狠毒,并非封锁你的搜索,而是如果你的文件信息内有这些关键词,那么文件共享消息就不会被发送到服务器上,如同这个文件没有被共享一样。这样既没有用户会发现被封锁的事实(因为有少量其他客户端的数据会被检索出来),又能达到封锁的目地。
当然,贝壳理解VeryCD这帮人的苦心,毕竟他们还住在中国,不过估计从此后,贝壳和朋友的机器上不会装VeryCD了。

2008年9月22日星期一

百度、官员辞职和特供局

这几个东西有什么联系?其实没什么大联系,只是同样是没用的东西而已。
先说百度吧,大家可能不知道,google的一个原则就是“不作恶”。简单来说,不因为人为的理由而改变搜索结果。包括调整排名,屏蔽关键词等。google卖的关键词和百度不大一样,百度的是改变搜索结果,google则是在旁边显示广告栏,其显示特征明显不同于正常搜索结果。从商业角度来说,卖排名当然不如卖广告收入多,但是google的收入却比百度高很多。我以前也完全无法理解google费力坚持不做恶的理由,更无法理解为什么google的收入会远远超过百度,然而这次却明白了。大家应当听说某奶粉品牌300万搞定某搜索引擎的事情吧?当然,某搜索引擎否认了,我们也没有什么证据来说明这次的却是被搞定了。但是,百度卖排名是事实,卖关键词屏蔽也不是什么很难理解的事情吧?也许这次没有被搞定,然而在卖屏蔽却没有什么异议。然而如果这么说的话,那么百度,无疑就成为了遮盖大众知情权的帮凶了。当然,同样的问题google并非没有。google在进入中国后,和政府达成了协议。部分涉及国家的敏感词汇被封锁,大家可以看google最下方的提示。“据当地法律法规和政策,部分搜索结果未予显示。” 如果出现这个,就代表你的关键词有问题,部分内容被屏蔽了。作为进入中国的代价,我们理解这种事情。然而即使是这种程度的改变,依然被很多google的 fans质疑,认为google背叛了“不作恶”的原则。大家可以想想,一个搜索引擎受到国家约束而被迫改变结果尚且要引来怀疑。这时候,一个可以用钱买卖,结果改来该去的引擎,有用么?
因此我强烈的建议大家,使用google的搜索引擎。或者说,当你需要知道什么事情,怀疑什么事情,而这个事情又和强权的利益相关的时候,请使用google搜索引擎,因为百度可能被“买通”了。你不知道你搜索出来的“最有名”的培训公司是否真的好(这是我一个朋友的真实例子,他所在的培训公司就是打的百度排名,但公司有严重问题),你不知道你“搜索不到”有问题的词是否真的只是因为你个人的问题(例如这次的三鹿)。当你搜索到的东西其实只是一堆利益的集合的时候,我觉得这个搜索引擎也就没用了。
我当然不是对百度有意见,也不是对国产有意见。我本人就是中国的程序员,也有朋友在百度任职。然而中国的公义(好吧,这个词大了点)是不能因为利益而含糊的。说的更准确点,当你为了利益而含糊公义的时候,说不定你的儿女正在喝三鹿奶粉。
下面讲讲官员辞职。这次溃坝辞职的孟学农,其实是第二次辞职了。头一次是因为在北京,非典处置不力而辞职。这次又在山西,因为溃坝辞职。中国官方的解释是,孟学农的两次辞职为做太平梦的太平官敲响了警钟。然而贝壳要问,为什么辞职了两次?
大家理解官员辞职,基本就是,辞去职务。辞去后干什么?我们并不清楚。当然,辞职后又重入公职,说起来也不是不可以。但是,这样真的可以么?有人可能说说法规并不反对,但是我想问,一个已经有了明显的处置不力前科的人,为什么那么容易的又进入了公务员队伍呢?难道中国对农村户口的人进入公职防范的那么严格,对有处理问题不力前科的人反倒不必防范?辞职,下台躲躲风头,学学陈冠希,事情走了再出来,损失的只是以前积累的名气,是不是太容易了?进一步说,这次因为三鹿辞职的某些官员,下次又准备在哪里任职呢?大家明天又想喝些什么呢?
当然,还有一个更坏的理由,就是这些人因为没有背景,被屡次作为代罪羔羊。为什么说比辞职重新担任公职更糟糕呢?因为这代表真正应当负责的人屁股都不必动。辞职重新担任官职,好歹还有躲风头的时间,还要损失自己的名气和政绩。作为官员,在做事情前还是要想想这个代价的。然而花钱弄一堆不做事的替罪羊养起来,出了问题就让他们辞职顶罪,然后再给他们重新安排一个替罪羊的职务。这连想代价的时间都省了,反正这帮人的工资是国家支付,人民出钱。拿人民的钱来糊弄人民,没有比这个更好做的决定了。如果真的碰到这种情况(当然,这只是贝壳的猜测),那官员问责机制就会从“没什么用 ”彻底变成“什么用都没”。
最后一个,讲讲特供局。特供局的意义相信大家都清楚,就是给皇帝弄贡品的单位。这种局弄出来,肯定要被大家骂的。当官的只管自己吃喝好,不管老百姓生死。有人还这么说,只要特供局存在一天,当官的就不会真正在意老百姓的生死。只要产品安全到家了,总理可以在大街上随便买个番茄吃了。在这里贝壳不打算讨论政府和食品安全问题,只是想问另外一个问题。让一帮人浮于事吃拿卡要的家伙负责另外一帮同样家伙的食品,有用么?
以前给皇帝拿贡品的时候,下面的经手太监经常吃拿卡要雁过拔毛。只要给一定的好处,就可以打上贡品的标签。多收了是朝廷付钱,好吃的自己先吃掉,反正皇帝也不会来查——要是他有这个空,肯定自己来管了。最后经常会出现朝廷的采买太监吃的比皇帝都好的现象。同样的事实难保不会再特供局出现,毕竟东西经过特供局后一般不会再交付其他部门检查。那特供局完全有可能检验一下,扣下一些特别好的,收钱定贡品。反正只要交上去的东西没问题,就没人问他们负责。

2008年9月21日星期日

紧急修复

贝壳周四的时候收到消息,烟台的系统崩溃,于是在24小时之内走了一趟天堂和地狱间的旅行。
开始的时候,贝壳在查一些业务有关的资料。期间和一个同事开了几句玩笑,但是发现他一脸便秘的样子,和我说没空。贝壳很郁闷,怎么这么没面子?过了几分钟,事情就发展成贝壳也一脸便秘的没胆子了,原因是烟台的系统崩溃。由于远程无法连接,只能让客户去机房重起整个系统。可重起后也没有反应。于是贝壳怕了,马上通知了老板。老板马上做了决定,要我们当时飞去烟台,并且在几分钟内给我们搞定了机票。于是在贝壳头处理紧急问题的时候,就受到了"飞机-出租- 零反应"的待遇。
中间首先要感谢一下给我们做Oracle技术支持的纪锋老师,这次如果不是他的大力协助,恐怕问题不会这么快解决。我们在零时间往烟台赶的时候,纪老师也马上打车往机场走。我们是五点接到的问题通告,五点半就联络好了各种问题,乘公司的车子往机场走(主要怕下班高峰不好打车)。六点多点的时候,我们拿到了登机牌,去做安检,然后顺便讨论起问题原因。当时认为基本不可能是软件问题,因为软件问题重起后基本都可以解决,也不会弄的机器停机(这个最终被检验是正确的)。可能是维护问题或者硬件问题。按照机器安装时间来计算,硬件问题的可能居多(系统才刚刚交付几个月)。
飞机是8点50在烟台落的地,落地后我们心急火燎地坐出租往报社赶。车刚出机场,收到一个消息,问题消失了。我们顿时安心很多,要是问题继续出现导致更严重问题,怕我们全都吃不了兜着走。现在,虽然我们还要去找出根本原因,可总比被客户拷问着检查系统来的好的多。到了报业后,我们先检查了系统。第一个被发现的问题是备份机已经满了,怎么会这样?系统的设计容量是三年500G,按照现在的数据量估计,最高不会超过30G,可备份机上足足有100G的空间!我们倒推了数据,发现备份要用140G以上的空间。怎么会这样呢?
原因我们没有找到,不过按照纪老师给出的原因,是备份的时候大量的归档日志造成了数据量暴增。但是备份暴增怎么会造成系统不能访问呢?贝壳陷入了奇怪的感觉中。虽然直觉上觉得就是这个理由,但是实际上却无法确定。按照我和同事说的话,如果用这个理由来说服我,是无法说服的。但是如果在目前让我给出一个理由,恐怕只有这个了。当天比较晚了,因此没有进一步分析,只是让纪老师调整了备份策略就去睡觉了。
第二天,贝壳仔细检查了所有的系统日志,找到了真正引发错误的理由。Linux9号错误,原因是因为文件无法访问。可是,究竟为什么造成9号错误呢?又是为什么导致重起后错误不消失,过后错误又莫名消失呢?进一步分析日志找到了这后两个问题的理由,客户重起节点1未完成时,直接重起了节点2。RAC似乎在所有节点同时失效后无法自动重连,即使重起也不行,必须重起客户端。最后按照数据倒推,认定问题在本地磁盘耗尽上。只是开始为了检测数据库备份,执行了 crosscheck,释放了部分磁盘空间,因此查不出来。
从这次事故恢复来说,最大的问题在于客户那里没有人及时进行系统维护,最终导致了磁盘耗尽。因此说做一个系统简单,然而要长期维护系统,恐怕就没这么简单了。

2008年9月15日星期一

中国又出事了

这次是毒奶粉,08年真是多事之秋。贝壳分析了下,估计会进入世界十大事故的行列。
有这么严重么?有。到底有多少奶粉有毒?有多少流入了市场?有多少没有追回?当局能统计出来么?统计出来你信么?如果统计不出来或者你不信,那么我说有可能被做成了月饼进了你的肚子,或者被北京的残奥运动员吃了,你信么?
三鹿的销售体系,是很难统计出最终消费者的,因此我们对比台湾统计的结果。台湾从三鹿进口了有问题的奶粉1000袋,有600袋上下剩余,400袋上下销售,其中没有一袋是进入婴儿市场的,全部被做成食品进了大众的肚子。按照这个比例计算,三鹿出问题的没出问题的奶粉,到底有多少被做成食品,进了你我的肚子呢?谁也说不清楚。月饼,面包,冷饮,这些我们每日要吃的东西里面到底有没有问题呢?谁也不好说。毕竟现在食品成本上升,使用廉价的奶粉来替代昂贵的原料,恐怕是很多企业的第一选择。
这次风波问题更大的在于,还有很多残奥运动员在北京,他们的食品呢?你我当然知道,他们是吃不到廉价的奶产品的。问题是,他们是否愿意相信呢?如果这个问题没有一个很好的解答,恐怕中国这次的运动员餐赔本卖吆喝要变成赔本卖骂名了。
还有这次问题的发生原因,按照三鹿本身的说法,是不法分子造假。那么不说出问题的总数是多少,三鹿说过,召回有问题奶粉700吨。就贝壳看到的资料,原奶和奶粉的质量比大约是8:1。按照这个比例,三鹿有问题的原奶大约是5600吨。这么大量的奶就完全没检查?如果说是部分有问题,到底多大比例?比例低了是不会造成影响的。高了,高了还是得问有没有检查。还有,一个企业,不同产品使用的奶源不一样么?如果一样,为什么只有特定产品出现问题?如果不一样,这会造成成本的升高,为什么不一样?
另外,三鹿集团在这次的危机公关上有严重问题,石家庄当地政府加重了这个问题,而国家又放大了这个问题。三鹿集团说八月已经发现问题,开始召回奶粉。那九月初问题刚发现的时候,发表三鹿奶粉产品质量没有问题的声明是怎么回事?奶粉召回,有没有通知消费者?(也许是中国没有这个惯例,我和一堆朋友说的时候,他们都一脸惊诧,召回产品要通知消费者么?)有没有统计影响面?国家的质管部门呢?免检产品是否真的不需要抽检?那质检是怎么选免检的?口碑?口碑怎么评价?质量历史?企业质量没有问题历史才是常规,有问题历史的要着重查。如果免检产品真的免检,那质检部门有什么用处?如果食品质量有问题造成生病归卫生部门管,那么飞机质量问题造成坠机是否归国安部门管,汽车质量问题造成车祸是否归交通部门管?质检证书造假是否应当归公安管?
实际上,这次事件引发的最严重的问题,在于政府,质量检验体系失去了共信力。根据调查,92%的人不会再选择三鹿的任何产品,6成以上的人对政府质量体系持不信任态度。那么,这次政府说没有对你们造成影响,你们信不信?下次,政府说你们的房子没问题,你们信不信?如果不信,大家准备吃什么?喝什么?

2008年9月10日星期三

语言的对比

最近贝壳在学(或者准备学)三门语言,python,ruby,scheme。全部都是高级抽象语言,都是脚本语言。加上贝壳本身已经非常熟悉的几门语言,C/C++,Java,Asm,bash,贝壳这次是正宗的要"精通"七门语言了。当然,如果加上勉强会用如vb这些,十多种也不成问题。做为一个学了多种语言用了多种语言的程序员,我想写一下自己多这些语言的认识,作为后来者的参考。
首先是C,当然,是不包含C++特性的C。这门语言可以说是贝壳所见的语言中,最强大最广泛最具备生命力的语言。其他任何一种语言说到混编接口,基本就是C语言接口。C语言也可以模拟非常多的特性,COM,C++,都可以用C模拟。当然,模拟的复杂程度是另外回事情。并且可以编写其他语言的解析器/虚拟机,而这点来说其他语言很难做到。其实本质上说,C语言就是跨机器跨平台的万能汇编语言,所以才具备这些强大特性。C的核心思想是指针,其整个语言构架都是基于指针的。通过指针,我们直接操纵着内存地址的读写。当然有利必定有弊,C语言太难掌握,效率太低了。使用C写代码,就是和机器打交道。你得控制数据的读出写入,控制设备的初始化,控制内核交互。根本上说,一个强大的C程序员,并不强大在算法和创意上,而是强大在对系统的方方面面的了解上,强大在对基础原理的掌握上。
其次是C++,说实话,这语言有点高不成低不就。如果要掌握系统的方方面面,他不比C。如果要抽象要构架要快速要敏捷,他比不上所有的抽象语言。但是 C++的长处在于在系统的层级上引入了算法抽象,增强了编码效率。如果你确定需要在系统层级上编程,又不高兴从C的角度去写。C++可以极大的增加你写代码的速度。当然,他的弊端就是对底层的掌控力和抽象实现的复杂程度。C++(我指的当然是包括STL和Boost的)的长处在系统层级,所以你写代码的时候必须了解抽象的实现方式。然而抽象实现的越强大(例如boost的share_ptr),其底层的机制就越复杂,你掌握的时间也越长。而如果不了解底层,往往会发生很多很奇怪的问题。例如smart pointer的问题,在其他语言中,这是语言系统的bug。而在C++中,则是你自己的问题。因此,想要真正玩好C++的程序员,必定首先是一个C高手,而不是拿着C++的OO特性把C++当普通OO语言的人。
再次是Asm,这种语言可以说没有什么生命力,因为他变化的太快了。一旦硬件构架改革,汇编就要调整。而且抽象层级不够高。除非你正好做操作系统底层,编译器优化和系统破解,否则最好不要考虑这种语言。这种语言的核心构架是寄存器。
然后是java,当然,还有很类似的C#。这类语言开始,语言的抽象层次提高了,因此可以提供反射(python中叫做自省)。反射是高级语言中必要的特性,然而C/C++并没有提供,原因么则必须说一下编译型和解释型语言。一般语言分为编译型和解释型,编译型的代码成型后只需要相关的库支持(其主要目的是代码复用减少复杂度和内存消耗),而解释型的语言需要解释器。如果仅仅从方便使用角度说,解释型语言远远不如编译型(因为要单独安装解释器,当然,像 bash这类怪胎就表说了),而且解释型的语言运行效率仅有编译型的1/10左右。然后当今语言界,编译型语言远远比不上解释型使用广泛。其根本原因有三个,一个是编译过程,一个是反射,最后一个就是内存管理。
如果读者有编译代码的例子,应当知道,除非在同等的条件下,否则编译C++代码是很麻烦的事情。而配置同等的编译条件则彻底失去了C++跨平台的意义。因此C++在不同平台下反复编译的时候,需要考虑大量的问题来达到跨平台的目的。往往这种事情麻烦到需要作者亲自指导编译的地步。如果一个软件,发布的时候声明可以跨平台,然而使用前需要作者指导用户做一堆繁复的操作。估计这个软件只会受到专业用户的欢迎吧。
所谓的语言编译,其实需要经过两步,编译和链接。编译的主要目的是将每行代码翻译成对应的汇编语言,而链接则是将符号引用转换为地址引用。举例来说,我使用了一个变量str来存放一个字符串,这个变量str就是一个符号。我需要在上文中声明(declear)这个变量,以便编译器在编译的时候代换这个符号对应的内存地址,和理解如何使用这个符号(关于上文没有声明的情况下的错误,请看"向下引用"特性)。我使用str的第四个字符的时候,str[3]就会被翻译到固定的内存地址或者基于基址的偏移,机器完全不用理会str是什么。而这点,则是反射实现的最大障碍。反射可以提供一个对象是什么,有什么的信息,并且可以动态创建对象。有了反射以后,才可以实现序列化,分布式等等高级应用。而C类语言在编译后失去了符号是什么的信息,只剩下一个名字,链接后连名字都没了。这种情况下,你怎么知道一个对象是什么呢?
Java/C#可以提供反射,因此属于解释型语言。但是他们又不属于完全的解释型,而是解释型的一个特殊分支,中间代码。中间代码型的语言,需要编译,执行的时候又需要解释器。看起来没什么好处,可是在支持反射的基础上,大概可以以C代码1/2的速度运行,比纯解释快多了。原因何在呢?我们可以看看 C++为什么不支持反射。反射是保存针对执行结构的数据并且提供交互,而C++则在编译时生成后丢弃了这些数据。因此,理论上说只要保存了这些数据就可以实现C++的反射。这就是中间代码语言所做的事情。当然,考虑到跨平台特性,编译的结果并不是汇编代码,而是类似汇编的代码(Java叫P代码,C#叫 IL)。后JVM直接执行P代码,C#则通过引擎编译IL到本地代码。因此JVM执行的时候效率基本恒定,而C#初次执行速度慢,后来则是比C++慢不了多少。
最后就是Java/C#的最强特性,动态内存管理。使用这个特性,可以使得程序员彻底的从内存分配和管理的泥潭中脱身出来。白痴的程序员写的程序可用,强大的程序员写程序的效率提高。可是成也萧何败也萧何,内存不到底不回收,又有额外的内存开销,结果导致系统的缓存命中率下降。我们平时觉得Java类语言执行慢最大原因在这里,半解释才不是根本原因。
因此这些语言的特点就是中间代码,其核心思想是对象。这类语言的最大特性就是抽象和构架,使用强大的设计模式,将大型问题拆分成多个小型问题解决。在解决问题的时候,其代码量并不比C++少多少。
再然后是python和ruby,当然,某种程度上还有bash,只不过他弱了点。这类语言的核心思想是抽象数据,例如字典,字符串等。bash是围绕着字符串处理设计的,python是围绕着集合设计的。这些语言解决问题的速度非常快,但是模块化特征和抽象特征相对弱。一般情况下,和C++相比,解决问题的速度大概是1:5,代码量则是1:3。

2008年9月9日星期二

程序员的几个分类

程序员有高下之分,可高下怎么分?到底什么是高程?程序员要完成哪些任务,怎么评价是否完成的很好?
下面由贝壳同学来胡诌一下他的个人感想,以下的程序员都指商业程序员。当然,爱好者可以类推。
首先我们先讨论一个风马牛不相及的问题,程序值钱么?废话,人家账单大门兄都已经是世界首富了。可贝壳认为,程序不值钱,算法也不值钱,如果说值钱的话,就和软件光盘上面的光盘一样,是个成本性质的辛苦钱。软件里真正值钱的是软件的思想,我们大可以想象一下,写一个让人想穿脑子也想不出怎么用的程序——当然,会写的程序员想穿脑子——然后想想值钱不值钱。大家就会了解到,其实程序员,编程本身,是和装配线上的装配工一样的低附加值行业。不同之处在于,不同培训程度的人劳动生产率不同,而且生产率差异远远大于普通行业而已。好的Coding比差的生产率会高上数倍,而且很难多找几个差的来替换好的,水平不足。但是,做Coding,无论做多快,其价值只会线性增长。
那么为什么软件也被称为今年来发展最迅猛的产业呢?其实关键在于软件业让人可以实现以前无法实现的一些想法。例如,以前不会有人能做到让地球上所有的人 (好吧,是大多的人)坐下来一起讨论一个事情的方法。那么,今天我们的技术已经可以做到。有个人针对当前的技术,设计出一个很好的让所有人坐下来讨论事情的系统。包括一个BBS,带自动翻译系统。在线视频会议中心,可以附加购买在线翻译。一个邮件列表,带存档功能。一个文档编辑和管理系统,带同步编辑,版本管理和资料索引。当我说出上面这堆东西的时候,可能有的人已经晕了,当然,六牙四皂小姐估计已经不继续看了。不过做过一些时间和电脑有关商务的童鞋应当都理解这些东西的意义,并且可以想象这些东西带来的便利。在线翻译的支持系统,邮件列表存档系统,同步编辑,版本管理,资料索引,这些都是技术。尤其是资料索引和自动翻译,更是技术的巅峰之作。可是如果不是结合起来让客户用的舒服,这些东西有价值么?
软件业的价值在于将技术转换为客户的满意,并且最终转换成客户的钞票。越成功的规划,越能满足更多的客户,并且让他们付更多的钱。从这个角度讲,不论软件做的怎么样,微软的规划是全球一流的。同时,能促进这个过程的人,才是具有价值的。不过遗憾的说,到这步基本就不是程序员,而是CTO了——
程序员的最高价值,在于根据技术和行业,判断应当发展什么技术,采用什么框架,从而低成本,高效的做出让客户满意度最高的系统——而且不是一个系统,而是一堆。这有两个非常苛刻的要求同时存在,对于技术非常熟悉,视野开阔感觉敏锐,否则怎么去感觉技术的价值,判断应当研发的技术?就这点而言,许多程序员在超过30岁后往往都可以在熟悉的领域内做到,有条件的话大概可以在多数领域做到。然而最麻烦的在于,做这个事情的时候,你必须熟悉客户,熟悉客户需要什么。这点往往是不可能的!客户是不可理喻的!技术不是万能的!要知道,使用最好的技术,设计你最喜欢的系统,往往是客户最讨厌的事情。行业客户如此,通用客户更如此。
如果上点你做不到,自然有高水准(也许吧)的人来做,那么你可以做次之的工作。什么呢?执行他们制定的方向。上层的人会告诉你应当发展什么技术,采用什么框架。现在要求你——不用会——能够整合实现这个目标。说明白点,你可以招,但是要求能留下人,低成本。你可以培训,但是要求能做事。你可以研发,但是要求好用。你可以买,但是要求低成本。如果你能实现这些目标,那么同样,你也是有价值的。
这个层次的程序员往往是Project Manager(当然,很多PM根本不是程序员)/Team Leader/Core Programmer。对于他们的要求往往是兼顾技术,行政和人事的。他们需要能够组织研发,积累技术,产生产品。很遗憾的,个人编程能力往往又是次之。不过程序员是很艺术化和个性化的一群人,在这个位置上的人,如果没有相当的技术水准,很难镇住下面的人,用普通管理人员来管理程序员的结果往往是给程序员联合起来耍。因此一般情况下也需要了解大致的程序,并且最好有一定技术水准。
最下面一个层次的人,基本就是能够实现程序,会用上面决定的框架和技术,编码效率高,工资要求低——别的没了——
当然,以上是从职业分工来讲一个职业程序员的价值的,你可能说上面没道理,贝壳乱讲。不过事实是,职业的情况下就是上面的状态。当然,从业余爱好者,技术研发者来说,程序员又有另外一种不同的分法。
第一个层次,是刚刚学会技术。能够使用某种特定语言,按照一些例子编写一些程序。实话说,按照贝壳的程度,入手一般语言做到这点不超过7天。然而很多人会徘徊在这个水准无法进步,原因在于——他们能写程序了,而且写了能用。从这个引申开来,顺便说一下,程序员的进步是个很吊诡的事情。一方面来说,要勤于钻研技术才能进步。但另一方面来说,如果不够懒,是很难有足够动力学程序的。因此,好程序员都是勤于钻研的懒汉。
第二个层次,学会了使用框架,并且能够设计一些中度复杂的系统,开始接触第二语言。和初级程序员不同的是,他们能实现一套完整的系统,而不是一个个零散的功能了。这要求他们了解框架,什么时候触发什么函数,系统间怎么互相通讯。并且,有水准的还可以写一些小型的框架。
再上一个层次,了解软件工程对软件的意义,能够跨多种语言编程,灵活使用设计模式,能够设计复杂框架,习惯文档化。和上面的区别看起来不大,不过是能多用几种语言,朴素的设计被设计模式所规范,设计的框架复杂化,并且会写一堆无聊的文档。不过从这步开始,程序员开始了迈向大道的第一步,在这个层次以下的只能算爱好者。无论是研究技术,研究数学理论,还是什么,规范化都是必须而且是非常重要的。我们很难想象一堆工程师,各画各的图纸,最后房子还建的多快好省的。同样,作为高级程序员,头一步就是学会和别人合作。使用设计模式的规范进行设计,使用文档描述系统,可以跨越多种语言协作,了解多种语言思想,这是必须的。
再上一个层次,就已经不是程序员的境界了。作为程序员,上个水准已经到头了。更强的程序员意味更规范?效率更高?那是八级钳工!作为程序员,你可以不认识英文,你可以大字不识一个,然而你必须是个数学高手(其实现在数学高手大字不识一个几乎不可能)。作为程序员的巅峰,你可以很轻易和他人协作,使用合适的语言,然而无法规避的是对问题的抽象描述和求解。在贝壳作为程序员的这段时间里,无数次的碰到数学问题,有些往往是大学里面我们所不屑一顾的。例如蒙特卡洛法,拉格朗日乘子算法,这些在程序里面都有很重要的应用。有的时候更要自行抽象数学模型,并且设计满足时间限制和空间限制的解法。能够抽象问题,解决问题的,才是真正的技术系的高手。
OK,上面,贝壳从两个方面(工程和技术)论述了程序员的高下之分,作为他胡诌的结果,他目前的水准大致是——不知道。并且很遗憾的告诉大家,目前贝壳能看到的就这么多,再上面是什么样子——要么等到了再告诉您?

2008年9月6日星期六

C++下的Variant

所谓C++语言,是一种强类型语言。即是说,C++种的某个变量,在使用时类型是已经确定的。这个并不是设计者的喜好或者是偏心,而是C++中的变量都会被翻译成准确的内存地址和大小,如果类型不确定是不可能处理的。但是在事实中,我们经常要处理一种"变类型"。例如,我们可能需要解析表达式,这个时候我们可能用一个或者两个栈来解决这个问题。可栈里面塞的东西就精彩了,对象,函数,数据,都在里面。这时候,如果是python,我们可以直接用list,他是弱类型的。但是C++怎么办?
一般来说,我们会使用Variant类型来解决这个问题。这是C++面对对象机制和算子机制所派生出来的产物,能够让用户自行定义对象的行为。如果一个对象,可以表现的像这个又像那个,那不就解决问题了?因此在COM中就有一个variant。不过贝壳看过机制,是一堆东西的集合,非常的不美丽。今天贝壳又看到一个variant的实现,漂亮多了。
废话少说,上代码。
#include
using namespace std;
#include
using namespace boost;

int _tmain(int argc, _TCHAR* argv[])
{
any a;
a = 10;
printf ("%s: %d\n", a.type ().name (), any_cast(a));
a = 10.5;
printf ("%s: %f\n", a.type ().name (), any_cast(a));
a = string ("str");
printf ("%s: %s\n", a.type ().name (), any_cast(a).c_str ());
return 0;
}
当类型错误时,出现bad_cast exception。

2008年8月27日星期三

python的性能问题

贝壳最近在一个朋友的网站上看到了关于SICP零钱兑换问题的python求解,使用了记忆机制,然后他给出了代码。然而他的代码计时上有点小问题,也没有用包装器(奇怪的是,有写),而且python的栈深度有限。因此贝壳做了几个修改的版本,需要测试下性能,下面就是关于性能的几个问题和过程。
本文详细论述了python语言下和C++语言下使用各种方法测试代码性能的方法,以及粗略的关于两种语言不同算法性能对比。
原始的python代码是这样的:
def change_coins(money):
first_denomination = {
1:1, 2:5,
3:10, 4:25,
5:50,
}
def cc((amount, kinds_of_coins)):
if amount == 0:
return 1
elif amount < 0 or kinds_of_coins == 0:
return 0
else:
return cc((amount, kinds_of_coins - 1)) \
+ cc((amount - first_denomination[kinds_of_coins], kinds_of_coins))
print "change_coins return %s" % cc((money, 5));
return ;
利用记忆原理包装后是这样的:
def memoiza(fun):
cache = {}
def proc ( *arg ):
if cache.has_key(arg):
return cache[arg]
else:
x = fun( *arg )
cache[arg] = x
return x
return proc

def decorator_change_coins(money):
first_denomination = {
1:1, 2:5,
3:10, 4:25,
5:50,
}
@memoiza
def cc(amount, kinds_of_coins):
if amount == 0:
return 1
elif amount < 0 or kinds_of_coins == 0:
return 0
else:
return cc(amount, kinds_of_coins - 1) \
+ cc(amount - first_denomination[kinds_of_coins], kinds_of_coins)
print "decorator_change_coins return %s" % cc(money, 5);
return ;
不记忆,利用栈模拟递归展开是这样的:
def native_change_coins(money):
first_denomination = {
1:1, 2:5,
3:10, 4:25,
5:50,
}
stack = [(money, 5)];
rslt = 0;
while len (stack) > 0:
param = stack.pop ();
if param[0] == 0:
rslt += 1;
continue;
elif param[0] < 0 or param[1] == 0:
continue;
else:
stack.append ((param[0], param[1] - 1));
stack.append ((param[0] - first_denomination[param[1]], param[1]));
continue;
print "native_change_coins return %s" % rslt;
return ;

贝壳主要需要测试上面三个代码的执行效率和瓶颈,所以贝壳用的主代码是这样的:
import time
import timeit
import profile

def test_func(f):
f (300);

if __name__ == "__main__":
t = timeit.Timer("test_func (change_coins)", "from __main__ import *");
print min(t.repeat (5, 1));

t = timeit.Timer("test_func (decorator_change_coins)", "from __main__ import *");
print min(t.repeat (5, 1));

t = timeit.Timer("test_func (native_change_coins)", "from __main__ import *");
print min(t.repeat (5, 1));

profile.run("test_func (change_coins)");
profile.run("test_func (decorator_change_coins)");
profile.run("test_func (native_change_coins)");

下面是部分结果:
change_coins return 9590
1.22809910198
decorator_change_coins return 9590
0.00217178440277
native_change_coins return 9590
2.69215193551

以上是时间测试结果,使用timeit模块来测试运行时间,重复5次,取最小值。具体原理可以看dive into python,详细请看上面的代码。从结果中我们可以看到,使用记忆技术后,性能提升了500多倍,这是符合规律的。然而使用了集合模拟栈之后,性能大幅下降。下面我们看看为什么。

change_coins return 9590
1292596 function calls (6 primitive calls) in 13.591 CPU seconds

Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)
1 0.059 0.059 0.059 0.059 :0(setprofile)
1 0.000 0.000 13.533 13.533 :1()
1 0.000 0.000 13.533 13.533 amount.py:102(test_func)
1292591/1 13.531 0.000 13.531 13.531 amount.py:11(cc)
1 0.001 0.001 13.533 13.533 amount.py:5(change_coins)
0 0.000 0.000 profile:0(profiler)
1 0.000 0.000 13.591 13.591 profile:0(test_func (change_coins))

decorator_change_coins return 9590
2494 function calls (881 primitive calls) in 0.027 CPU seconds

Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)
873 0.004 0.000 0.004 0.000 :0(has_key)
1 0.000 0.000 0.000 0.000 :0(setprofile)
1 0.000 0.000 0.027 0.027 :1()
1 0.000 0.000 0.027 0.027 amount.py:102(test_func)
1 0.000 0.000 0.000 0.000 amount.py:51(memoiza)
873/1 0.013 0.000 0.026 0.026 amount.py:53(proc)
1 0.001 0.001 0.027 0.027 amount.py:62(decorator_change_coins)
742/1 0.009 0.000 0.026 0.026 amount.py:68(cc)
0 0.000 0.000 profile:0(profiler)
1 0.000 0.000 0.027 0.027 profile:0(test_func (decorator_change_coins))

native_change_coins return 9590
3877778 function calls in 38.798 CPU seconds

Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)
1292590 5.824 0.000 5.824 0.000 :0(append)
1292592 5.960 0.000 5.960 0.000 :0(len)
1292591 6.076 0.000 6.076 0.000 :0(pop)
1 0.000 0.000 0.000 0.000 :0(setprofile)
1 0.000 0.000 38.798 38.798 :1()
1 0.000 0.000 38.798 38.798 amount.py:102(test_func)
1 20.938 20.938 38.798 38.798 amount.py:80(native_change_coins)
0 0.000 0.000 profile:0(profiler)
1 0.000 0.000 38.798 38.798 profile:0(test_func (native_change_coins))
以上是白盒分析结果,使用profile测试,主要分析函数的调用花费。具体可以参考http://www.sqlite.com.cn /MySqlite/11/480.Html。从上面的报表中,我们可以看出,最初的函数执行时间全消耗在了cc上。而记忆后,则是proc和cc基本对半,有的时候has_key测试也花点时间。这表示cc花费的时间大幅下降,记忆技术则花了比较多的时间。而模拟的呢?大部分时间都花在了 append,len,pop这三个函数上!这说明原始集合的效率严重制约了模拟效率。如果要提升性能的话,使用其他的集合吧。
另外贝壳又用C++写了一个,如下:

const int coin_map[] = {
1, 5, 10, 25, 50
};
const int coin_count = 5;

int cc (int amount, int kind_of_coins)
{
if (amount == 0)
return 1;
if (amount < 0 || kind_of_coins <= 0)
return 0;
return cc (amount, kind_of_coins - 1) + cc (amount - coin_map[kind_of_coins - 1], kind_of_coins);
}

int dd (int amount, int kind_of_coins)
{
if (amount == 0)
return 1;
if (amount < 0 || kind_of_coins <= 0)
return 0;
int rslt = 0;
for (int i = 0; i <= amount / coin_map[kind_of_coins - 1]; ++i)
rslt += dd (amount - i * coin_map[kind_of_coins - 1], kind_of_coins - 1);
return rslt;
}

class keys{
public:
int amount;
int kind_of_coins;
keys (int amount_p, int kind_of_coins_p):
amount(amount_p), kind_of_coins(kind_of_coins_p)
{}
bool operator == (const keys & k) const{
return (amount == k.amount && kind_of_coins == k.kind_of_coins);
}
bool operator < (const keys & k) const{
if (kind_of_coins == k.kind_of_coins)
return amount < k.amount;
return kind_of_coins < k.kind_of_coins;
}
};

map mCache;

int ee (int amount, int kind_of_coins)
{
if (amount == 0)
return 1;
if (amount < 0 || kind_of_coins <= 0)
return 0;
keys k (amount, kind_of_coins);
map::iterator iter = mCache.find(k);
if (iter != mCache.end ())
return iter->second;
int rslt = 0;
for (int i = 0; i <= amount / coin_map[kind_of_coins - 1]; ++i)
rslt += dd (amount - i * coin_map[kind_of_coins - 1], kind_of_coins - 1);
mCache.insert(pair(k, rslt));
return rslt;
}

int _tmain(int argc, _TCHAR* argv[])
{
const int loop_times = 300;
clock_t s = clock();
printf ("kind of coins: %d\n", cc (loop_times, coin_count));
printf ("times:%d\n", clock () - s);

s = clock();
printf ("kind of coins: %d\n", dd (loop_times, coin_count));
printf ("times:%d\n", clock () - s);

s = clock();
printf ("kind of coins: %d\n", ee (loop_times, coin_count));
printf ("times:%d\n", clock () - s);
return 0;
}
注意到主函数中,使用的是clock来计量时间。如果C++下要做白盒性能测试就比较麻烦,需要用精确计时函数和宏。需要的可以单独和我联系。下面是部分计算结果,cc的和ee的,没有dd的。
300的计算结果
kind of coins: 9590
times:62
kind of coins: 9590
times:46
1000的计算结果
kind of coins: 801451
times:15953
kind of coins: 801451
times:11000
单位,ms。
原生的效率差异是20倍,用了缓存后性能只有略略上升?!反而是python比较快?
看来C++下的map效率也不高,要用hash_map才好。
倒是栈长度好很多,贝壳估计是131072次调用,大约是16384分。

2008年8月14日星期四

运气真好

昨天晚上吃完晚饭,觉得应该勤快点,还指不定什么时候回去呢,把衣服洗了。刚刚把衣服放进去,电话过来,说系统出问题了,要赶快去看看。于是,贝壳赶快出门,去看看系统有什么问题。
进去被一顿狂说,怎么这么不稳定,怎么解决。贝壳一个头两个大。马上去看看什么问题,一看把我气个半死。原来新华社的稿件都是一个xml,一个txt,一个图片,主文件名一样,贝壳的程序也是基于这个原理写的。现在到好,只有图片没有xml,或者只有xml没有图片。这叫贝壳做个P啊!据说是因为新华社要推新格式,所以老格式不怎么支持了。问题是,上面说保奥保奥,居然奥运期间来这手,这不是要整死人么?而且通知都没有的,要不过来看,黑锅就我背定了。
抱怨归抱怨,贝壳还是赶快改程序,做了个入新格式的。进度还挺快,虽然差异一堆,但是做到早上三点半基本就做好了。到实验机器上一跑,全部通过。然后到生产环境中一炮——全部报错。
OK,下面可就是贝壳无能为力的了。毕竟数据库那里应该都是一样的,而且也不是贝壳写的,无法调试。于是今天只有这样,回去睡觉。走到电梯里面,贝壳觉得不对,味道不对,一股臭脚的味道。这种味道只有两种可能,一种是中国男足来过了,一种是下雨。出门一看,果然,大雨滂沱。最要命的是,贝壳只带了两套衣服,还有套正在水池里面泡着~~~
于是贝壳一路狂奔,跑过去没两步,哗的一声,贝壳就不知道陷到什么里面去了。吓了一跳,赶快站直,包举高。仔细看看,原来前面马路修路,旁边的土还没有填完整,给水一冲就变成了泥浆坑。贝壳就是陷到这里去了,水刚好漫过小腿肚。
慢慢爬上来,然后贝壳就不敢跑了。前面毕竟还是有几个没有完成的井的。万一掉里面去,连申诉都不会有人管的,毕竟那是还没修好的工地。于是慢慢慢慢走回去,到宾馆的时候全身湿透,外带两脚泥。而且两套衣服全报销了,连第二天吃饭怎么出门都不知道~~~
奥运期间,这个运气还真是——无敌了。

2008年8月11日星期一

最近悟到了一个道理

贝壳问上帝,中国房价什么时候下来。上帝说,通涨结束,或者居民消费上去就下来了。
贝壳问上帝,中国居民消费什么时候上去。上帝说,全民保障体系搞好就上去了。
贝壳问上帝,中国什么时候结束通涨,搞好全民保障体系呢?上帝哭着说,我看不到那天了。

上面是拿中国足球的玩笑改的一个玩笑,不过贝壳真的悟到了房价高的原因。通涨乱高无比,保障一塌糊涂,赚了钱不敢花,也不能放,当然只有买房了。房价上涨,通涨更加高高高。

2008年8月3日星期日

程序员入门的12个问题

以下题目是应一个朋友问而写的,适用于刚刚入门有志或者有需要做程序的朋友的题目。题目脱胎于日常编程中常见的一些问题,很多是贝壳实际碰到问题的变形。题目不注重所用语言,每道题目可以用不同语言解决。有意思向计算机方向发展的可以试试用不同语言来解决,看看哪种语言最方便解决这种问题。如果打算增加难度的话,请使用C++来做,并且尽量抽象复用。在这个过程中积累下来的可复用代码会对以后编程有很大帮助。

1.读出文件中的以下格式内容,计算逆矩阵,并按照同样格式输出。
1 2 4.5
3 0 1
9 5 2
数字间以空格分割,行以回车分割。
难点:
输入和输出应当可以选择是键盘输入还是文件输入,输出到屏幕还是输出到文件。
逆矩阵计算中有可能求不出,出现除零。设法避免直接的报错。
评价:
很中规中矩的一个问题,有点竞赛的味道。只要做过程序的人一般不会失手。

2.某个XML,其中记录了一些信息。信息是按照时间-地点-人物的顺序记录的,例子如下:



...



现在需要你颠倒一下,变成这样的:



...



难点:
看看能想出多少解决问题的方法。
试试尽量减小内存消耗。
评价:
解决问题的方法很多,比较一下这些方法的优劣。
有一年以上程序经验的就可以最终解决,但要解决的比较完善需要两到三年经验。

3.下载google的首页,跟踪二级连接(二级连接,就是首页中连接指向的页面,上面连接指向的页面)。
并计算其中所有页面,显示出的非空白字符的个数。(显示的文字中的非空白字符)
难点:
试试看跟踪js脚本链接。
登陆后的google首页是不一样的,包括提示,语言类型,设法统计登陆后的首页。
如果是多级呢?
评价:
宽度优先和深度优先算法的应用,对集合运算有一定要求。
重点在于获取和处理html页面的方法。
一年以上即可解决,完善程度和技术水平关系不大。

4.运行两个程序,A和B,将A的输出输入到B中。
难点:
需要等待A的输出和B的输入,以及程序的终止条件。
评价:
需要对系统熟悉,知道管道和用法。知道进程间交互的API。
需要研究过系统,程序水平没有要求。

5.遍历某个目录,找出其中的特定图片文件。
难点:
怎么分析图片文件?文件名是比较粗略的方法,更好的是使用文件签名分析。
下次遍历的时候速度怎么提高(假定文件不变化)。
评价:
还是深度和宽度搜索题目,分析文件是难点。
扩展要求对于数据缓存有一定要求。
一年以上即可解决,文件签名分析看个人水平。

6.监视某个目录的变化,将新加入的mp3的相关信息(IDv3)邮件发给我。
难点:
怎么监视目录变化?
怎么提取MP3的内容?
怎么发邮件?
怎么保证不漏内容。
评价:
要对系统熟悉,了解mp3格式或者能够自行寻找库扩展语言。
了解邮件发送协议,或者能使用系统库发送邮件。
两年以上可解决,完善需三年以上水准。

7.写一个程序,可以计算加减乘除,支持括号。
难点:
让你的程序算算1+2*3,看看是多少。正确应当是7,设计不良是9。
看看你的程序,2/6*3得多少,是不是1.0(最好是1)。
让你的程序设法支持乘方和函数。
评价:
对数据结构和算法要求很高。
一年以上可解决,要扩展支持算符和算法,需要三年水准。

8.画一只乌龟,保存为图片。
难点:
让用户动手画?
试试保存为各种格式的图片。
评价:
实用项目,按照书本教程最多12小时就可以掌握。
然而需要自行解决并做好,至少一年以上。

9.写一个小桌球程序,让一个白球设法打落一个黑球。
难点:
注意屏幕闪烁。
写个9球游戏如何?
评价:
对物理知识有一定要求,对游戏常识(双缓冲)有一定了解。
扩展要求了解桌球规则。
两年以上水准。

10.写个小的管帐系统,告诉我这个月你钱是怎么花出去的。
难点:
随时记账。
评价:
系统并不难做,然而考虑实用性后,很少有系统能得到好成绩。

11.写一个小程序,向一个文件的第11字节写一个A,并将程序编译好后的大小控制在2K以内。(不得使用虚拟,脚本语言完成)
难点:
控制大小。
评价:
对可执行程序的结构和优化,系统库结构有相当了解,这是为C++语言设计的题目。
三年以上水准。

12.写个小阅读器。
难点:
能读HTML么?繁体呢?
小心编码问题。
ZIP包内的文件能读么?
评价:
最终的实用项目。
一年就可以做,但是做好至少要三年以上水准。

2008年7月30日星期三

avast4 collide with ext2ifs

Affected Product:
Avast4 home edition
ext2ifs 1.10c
ext2ifs 1.11
Description:
avast4 home edition is a free anti-virus tools. In 2008-07-30 it update some files, include some file called 'aswSP.sys'. According infomation in autoruns, it's avast self protection module.
[Here is info from autoruns.]
aswSPavast! self protection module ALWIL Software c:\windows\system32\drivers\aswsp.sys
[Here is info from update-log]
2008-7-30 7:36:14 file Direct move of file: C:\Program Files\Alwil Software\Avast4\Setup\INF\AMD64\aswSP.sys
2008-7-30 7:36:14 file Installed file:C:\Program Files\Alwil Software\Avast4\Setup\INF\AMD64\aswSP.sys
2008-7-30 7:36:14 file Direct move of file: C:\Program Files\Alwil Software\Avast4\Setup\INF\aswSP.sys
2008-7-30 7:36:59 system Reboot set by changed resident C:\WINDOWS\system32\drivers\aswSP.sys
2008-7-30 7:36:59 system Driver file copied: C:\WINDOWS\system32\drivers\aswSP.sys
If u use ext2ifs in system for share date with linux, it'll cause system crash with code BAD_POOL_CALLER. There is not evidence show it has connections with ext2ifs, but the crash always happen when I try to access data in a disk use ext2ifs. When I copy data to ntfs disk, it'll be all right. Here is dump analyze.
*******************************************************************************
* *
* Bugcheck Analysis *
* *
*******************************************************************************

BAD_POOL_CALLER (c2)
The current thread is making a bad pool request. Typically this is at a bad IRQL level or double freeing the same allocation, etc.
Arguments:
Arg1: 00000007, Attempt to free pool which was already freed
Arg2: 00000cd4, (reserved)
Arg3: 04030401, Memory contents of the pool block
Arg4: e13a7258, Address of the block of pool being deallocated

Debugging Details:
------------------


POOL_ADDRESS: e13a7258

FREED_POOL_TAG: pSsA

BUGCHECK_STR: 0xc2_7_pSsA

CUSTOMER_CRASH_COUNT: 1

DEFAULT_BUCKET_ID: DRIVER_FAULT

PROCESS_NAME: _uninst.exe

LAST_CONTROL_TRANSFER: from 80544e86 to 804f9aef

STACK_TEXT:
eb364b68 80544e86 000000c2 00000007 00000cd4 nt!KeBugCheckEx+0x1b
eb364bb8 ee072a0a e13a7258 00000000 8055a584 nt!ExFreePoolWithTag+0x2a0
WARNING: Stack unwind information not available. Following frames may be wrong.
eb364be4 805c5e1c 00000730 0000016c eb364cdc aswSP+0x5a0a
eb364c04 80639346 e3986008 0000016c eb364cdc nt!PsCallImageNotifyRoutines+0x36
eb364d08 805c5bcd 7c810665 00000000 00000000 nt!DbgkCreateThread+0xa2
eb364d50 805421c2 00000000 7c810665 00000001 nt!PspUserThreadStartup+0x9d
00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16


STACK_COMMAND: kb

FOLLOWUP_IP:
aswSP+5a0a
ee072a0a ?? ???

SYMBOL_STACK_INDEX: 2

SYMBOL_NAME: aswSP+5a0a

FOLLOWUP_NAME: MachineOwner

MODULE_NAME: aswSP

IMAGE_NAME: aswSP.SYS

DEBUG_FLR_IMAGE_TIMESTAMP: 4881fba3

FAILURE_BUCKET_ID: 0xc2_7_pSsA_aswSP+5a0a

BUCKET_ID: 0xc2_7_pSsA_aswSP+5a0a

Followup: MachineOwner

The crash happened in aswSP+5a0a.

Resolve solution:
There is not solution to resolve now. Uninstall avast, or uninstall ext2ifs.

以上内容的中文注释:
不要同时使用avast4和ext2ifs,尤其在今天的更新后。
会使用ext2ifs的,上面的东西应该也看得懂了,其余不翻译。

2008年7月16日星期三

今天25岁

今天贝壳25岁了。
前两天时间紧,赶快找两个人吃了顿当过25。今天早上起来,就收到了六牙四皂小姐的恭喜。这一刻,我总算了解她收到我一堆废纸当生日礼物时的心情了。路上第二个过来,是老妈的。到公司,兔子姐的第三个来了。然后,贝壳把名字改成了今天25岁。猫小姐的第四个和蛆小姐的第五个也来了。才五个,还有两个是提醒的,好失败。
不过无论如何,还是谢谢上述人,在我不长的人生中,还有你们记得我。

2008年7月9日星期三

新种病毒出现

有新种病毒出现,大家当心。
病毒症状如下:
有MSN好友给你传一个网址,如同http://[用户名].imagecroco.info/。(贝壳注:现在已经被Mozilla列为欺诈网址)当浏览后中毒,会继续给好友发送网址。发送网址时用户离线,发消息用户不回复。中毒用户提示已经在另外一个地址上登录。
机理估计如下:
当你访问网站时,会被要求输入用户名或密码。或者被挂上马,等登录时被套出用户名和密码。当你不使用时,服务器会自动使用你的用户名登录,给你的好友发送病毒。如果不修改密码,即使本机清理病毒或者设置名称提醒也未必有用。

贝壳房里的澡堂

贝壳住的旅馆,屋子的中心是一个浴室。透明玻璃的……
六牙四皂小姐已经参观过了,评价是很华丽。

2008年7月8日星期二

是我太无知,还是世界太疯狂

刚刚在用电脑,突然听到空调咔咔狂响(没错,不是硬盘),然后从里面飞出……一块冰?
我知道沈阳很凉快,不用这么提醒我吧。
是我太无知,还是世界太疯狂?

2008年7月3日星期四

沈阳记一兼我还活着

同志们,我还活着。最近项目不断,贝壳来了走走了又来,先是烟台,回上海修养两天再回烟台,然后回上海救火两天,连公司都没回去报销又来烟台,刚刚搞的差不多又来沈阳。下面据说还有上海和烟台,貌似奥运前是没完了。
而且烟台的网络状况急转直下。本来速度快的跟鬼一样,现在报社上网要用代理,每次打开个页面就要点一次密码确认,点记住密码也没用。这种情况下速度根本没意义,宾馆则是慢的跟鬼一样。然后……贝壳就光荣的断网了。
祸不单行,贝壳在7月1日接到了移动的通知,说话费只有14元多,记得充值。贝壳想,哦,也能用上一些时间了。谁知道第二天就断网,这才想起来移动的话费是第二日过的。然后紧急找人充值,却被告知因为欠费无法充值。然后贝壳要和客户联系,要用飞信帮人测东西,要收的通知,移动上网看blog……全部没了。最要命的是,我马上要出差沈阳。
贝壳到了沈阳,打车到位,一点不差,真的是运气。否则喊救命都不知道喊谁了。第二天,找个地方充值,总算把手机开开,谁想到刚开就用到了。我说要去故宫旁边的如家七斗星酒店,结果混蛋司机给我拉到了故宫旁边的如家酒店。下来才发现不是,回头找司机没影了。幸好这个时候,手机已经可以用了。于是我打开Moto A1200r,接入中国移动GPRS网络,使用Google Map手机版,很容易的找到了当前位置。(贝壳注,以上不是广告,当然和周XX的大XX里面那个MXXX和移XXX的XX更不一样……)。走走两分钟就到了,作为手机地图来说,这算是立了头功了。

2008年5月21日星期三

关于捐款的问题

上次贝壳已经给捐款不痛快过一次了,这次还是得继续不痛快。
首先是一条低调的新闻,网易终止与红十字会的合作,据说原因是"过程无法监控"。作为一个慈善机构,过程和结果无法监控,在发达国家(好吧,成熟国家,省得犯了某些人的讳)是不可想像的。网易为什么要提出监控,红十字为什么不给监控,这个就要说到善款的结算过程和意义,以及当前中国非政府组织的现状。
已经上班的朋友可能知道,我国有两种税征收方式,连带征收和查账征收。查账征收中,国家会检查企业的出入账目,计算企业盈利,并从中抽取企业收入的一定比例,这个就是所谓的增值税。如果您不小心亏本了,或者打平,那么是无需纳税的,这个很合理。总不会说亏本继续纳税吧。企业怎么减小增值税呢?主要是购买各种东西,当然是生产过程所需的东西,这些东西会计入成本(因此出差报销需要发票,因为这些发票代表着出差这个东西的成本)。如果企业收集很多发票,做帐做到打平,不就可以节税了么?事实没这么简单的,每个企业都有所谓的总支出,而总支出是无法作假的。因为一般的发票(餐饮,手机等一般人能搞到的发票)在总支出中占的比例不得大于一定的值。换句话说,做300W的生意,如果花45W作为餐饮花费是合法的,如果花100W,那就只能按45W报账。而其他发票可不是一般途径可以搞的到的,毕竟上游厂商也要开增值税发票,从源头的1元成本增值到100元产品,增长的99元里面的税收,不是落入上游就是落入下游,大家商量着来吧。从此意义上说,捐款(尤其是企业捐款)开具发票和财务透明就尤其重要。我举个例子。
例如,你是一个企业老板,做的是把一种东西买进,加工,然后卖出的生意。东西的成本是8元,加工的成本是2元,卖出去是15元,其中渠道和营销需要2元成本(原谅我用这种很白痴的例子),那么每个东西的净盈利就是3元。国家按照34%收税,简单点我们说税收就是1元(其实例子中还是有问题的,渠道成本超标了)。那么你每个东西纯利润就是2元。好,现在,你打算给灾区捐款,捐300(原谅我这种白痴比喻)。那么我们说,理论上你卖100个东西够么?不够!为什么?因为你要交税。你的纯盈利是3,可税交好就变2了,因此你要卖150个才够。
哪里有这种事情,我做好事还要缴税?这不是强盗逻辑么?所以一般企业捐款都要求开具发票,证明这笔钱是捐掉的。这样国家会把这笔钱计算为成本,不会让你交这种税。可如果捐款不给发票,那事情就有趣了。我做好事,还要缴税,而且很重。而且谁能开具这样的捐款发票呢?如果人人能开,那么好,我当场开个NGO,说是慈善,然后把企业盈利全部捐掉。这样我的企业永远打平(甚至可以亏损,享受国家补贴),然后钱还在我自己口袋里面。因此可以接受捐款的慈善机构也不是说开就能让他开的,否则会成为大企业的避税所。一般情况下,一个NGO如果要开,必须要公开账目,而后获得国家认可。公开账目是获得国家认可的必要前提,如果账目不公开,国家不会认可的,否则就会产生偷税。而中国的现状是,即使开慈善组织,账目公开,也很难获得国家认可。大家只能把NGO注册成公司,给工商界开一般营业发票,然后上税。因此大家捐款只能捐给红十字会,而他的账目却是不公开的,因为你没别的选择。
而且账目不公开会产生一个更混帐的后果。如果说前面一个只是不合理,那么这种可能就只能说是混帐加没人性。那就是贪污。我们捐100,NGO提取50%(或者更高)作为运作成本,然后剩下的发给灾民,对外宣称全发了。因为你根本不知道自己捐多少,自己拿的是多少。例如,你自己捐了100,你知道你的朋友捐了500,那么如果这个NGO说总数捐了1000,你怎么知道是不是真的只有1000呢?如果公开账目,你可以核对你的捐款是否在里面。很明显,如果不在,这是有问题的。如果在,而且每个捐款人的款项都在,最后的这个总数一定是正确的。发放也是同样的问题,如果公开发放账目,你可以看你拿到没有。如果有写没有拿,这是有问题的。如果每个上面写的人都确认收到了后面的款项,那总发放数也一定是对的。而后,我们通过总接收和总发放可以算出一个组织的组织运作成本。如果有大量的善款被消耗了,那么我们就可以说这个NGO是有问题的,我们会更换NGO捐款。而造成这种高消耗的最大可能就是贪污。同时,我们也可以计算出NGO的其他问题。例如,一个NGO的工作人员拿2W去买药品,他和药品供应商很熟悉(这个情况很普通吧)。所以让他们给1W的药品,开2W的发票。实际上就是给2W现金,开2W发票,给2W药品。1W给红十字会,1W实体药品自己拿回去分掉。公开账目后可以发现,这种情况下药品价格会比正常价格高一倍。如果说为地方增加产值后,自己贪掉一部分的贪污是某种程度上是可以接受的。如果说尸位素餐,人浮于事的贪污是让人痛恨的。那么拿灾民的带血的钱的贪污就是不可忍受的,伤阴德的。更粗俗的说,生儿子没屁眼。也许,我们的红十字会账目不公开有其他理由。也许,我们的红十字会大量提留是有其他原因。然而,你这个样子,让我怎么相信你?
最后就是关于捐款的数目。有人骂姚明捐的不够多,好,我想最好的方法是这个人站出来。我们计算下姚明同志的总捐款额度和拉到的捐款额度,比上全年除税总收入,再计算下你的总捐款额度和拉到的捐款额度,比上全年除税总收入。如果你高过姚明,我们随便你骂。捐款这种东西,要骂可以,站出来。我不反对攀比,我反对的是说别人捐少了,自己却不多捐。不过估计这些人也有郁闷的理由,据说国家政府机关是摊派捐款,有个银行每人要1000多。这些人估计就是这么郁闷出来的。

2008年5月16日星期五

关于地震的预报

贝壳这几天一直在关注地震的事情,其中经常能听到一个消息,其实地震前已经有了预测,只是因为奥运压了下来。而后贝壳在youtube上找到了一个视频"小动物曾经给予我们的警告!!(四川电视台新闻视频 )",是关于10号在四川电视台播出的新闻,其中就有大量出现蟾蜍的解释。这个事情让贝壳觉得很惊讶和伤心,莫非上万的人命不及政治任务?不过今天,贝壳在wikipedia上找到了这个事情的全面分析,可见wikipedia也不是全无是处么。
2008年5月10日,四川绵竹市西南镇檀木村(距离震中不到100千米)日前出现了大规模的蟾蜍迁徙,有数十万只蟾蜍在一制药厂附近公路上行走,但当地林业部门解释称,这是蟾蜍正常的迁徙。地震发生后,有网民十分激动,并留言指责“专家还不如蟾蜍”,而有动物学家亦认为,“动物感受地壳变动的能力较人类敏感,因此它们能预知自然灾害也不足为奇。”。但是经过网民搜索,发现蟾蜍大规模迁徙现象曾在2006年4月于重庆、2007年5月于河北唐山、2007年9月于山东临沂、2005年7月于吉林长春、2008年5月于江苏泰州、2007年4月于四川成都等全国大范围地区多次发生并被报道,完全不于地震相关,应属一种自然现象。
OK,上面我们可以看到,有这个报道是不假,可出现现象和发生地震间不正相相关。要是出现一次防震一次,的却非常麻烦。所以这次的事情,可谓事出有因。我们也可以想像一下国家的立场,如果确认有地震,跑不掉躲不开,那为何不报呢?一方面避免了人员财产损失,一方面展现了高科技,一方面还不会给奥运带来麻烦。所以我估计最严重是国家并不确认地震,因此为了奥运而不做可能性的防范。
不过由此我到想到一个关于地震预报上的缺陷。大家知道,我们国家很多事情是领导负责制。好不好,看领导。事情做好了固然领导有很大好处,可出问题领导也会倒大霉,因此很多人不求有功但求无过。唐山地震的反思中就有消息说其实地震前已经有了现象,可是领导对于不确定的东西不敢报,怕负责。毕竟地震这东西谁都说不好,就算我们看到各种景象,可万一不地震怎么办?对于地震这种问题,领导负责制是非常荒谬的。但是如果没有一个制度去平衡,恐怕我们又会陷入另外一个极端。地震局一有情况就报,也是怕负责。那我们会陷入漫天地震预报,就是不见地震的情况下。
对于这种情况,实话说贝壳也没有什么好的想法。不过如果让贝壳做选择的话,贝壳还宁可听到一堆未必发生,也不愿意听不到将要发生。大家可以想想,你是愿意多听几次下雨没碰到呢?还是愿意下雨前完全没预报呢?

2008年5月14日星期三

关于地震的问题

大家知道贝壳不是穷人(至少算起来,在社会上不算穷人),这次地震了,公司组织捐款。老实说,公司里面要是普遍捐1000的,贝壳最多也就挠挠脑袋,说句"半个手机又没了",就扔出去了。或者要是有说法,说你要带头怎么怎么的,贝壳最多也就是想想,捐了也就捐了。不过这次情况比较特殊,贝壳在出差,所以就出了点不愉快的事情。
因为贝壳在出差,所以无法直接捐款。公司组织了垫付捐款,让每个人讲个捐款额度,然后公司垫付捐款,回来再给。结果公司的一帮同事自己捐100,哄(发阴平声,一声)贝壳捐500。贝壳不想当这个出头鸟,所以就准备捐200。结果完后一统计,贝壳觉得不大对,怎么这多阿。赶紧问统计的同事,结果他回一句,你不是捐500么?
贝壳马上找负责捐钱的人联系这个问题,到不是说不想捐,而是要捐不要捐完全是我的自由,不经过我的同意怎么能随便说我要捐多少呢?大家可能觉得献爱心么,怎么还计较这个。实话说,要是哪个捐了自己一年工资,我随便你说这话,否则闭嘴。我高兴怎么捐是我自由,捐钱是捐钱,财务问题是财务问题。不经过本人同意就捐款,说起来回来让我认还是不认呢?认了就破财当出头鸟,不认回头还指不定别人怎么戳脊梁骨呢。这种随便让人一统计就捐钱的做法是否有点太不严谨了呢?
说到这里,贝壳还想起昨天看的一个笑话。一个照片,上面写,"XX慈善基金会请您捐款XXXX..."。说实话,昨天我是当笑话看的,今天我就有点笑不出来了。诚然,地震了,大家都很难过,我们想为灾区的人民做点什么,可做什么呢?怎么做呢?我的一个朋友在MSN签名上写,每次地震就捐款,捐款了就盖楼,盖楼了就回扣,回扣了就豆腐渣,豆腐渣了一震就倒,倒了继续轰轰烈烈的捐款。所以她的结论是,一分不捐。
我还是得强调一点自己的观点,每个人有捐的自由,也有不捐的自由,所以我觉得这个朋友的做法并没有什么错误。不过我们可以想想地震灾情最严重的是什么?学校。谁有听说政府机构有什么问题么?没有。要说缺钱,说地方贫穷,说着急上教育,说我们没办法。为什么死的都是孩子,而不是公务员?难道政府比学校更有钱?难道政府应该比学校更有钱?另外贝壳曾在哪里看过一个报道(请恕贝壳找不到原文),说这次受灾的聚源中学,被称为“危房”的旧校舍没事,新校舍反到倒了。对比对比各地的白宫式衙门,不觉得讽刺和悲哀么?
http://bbs.yaolan.com/thread_50222085.aspx
http://www.my1510.cn/article.php?e5d36d79e4f603f0
还有就是贝壳看到的一个资料,凤凰的节目,江河水走西南的记者写的blog。[http://www.my1510.cn/article.php?f734ba3f59d26040]其中就谈到了,过度的开发水利资源有导致地震的可能。关于这个问题,贝壳以前从未得知(当然,贝壳也不学水利地质,所以也不知道这个问题的具体情况),以前一直认为水坝这种东西,修越多越好。那么现在我们是否应当关注这些问题,关注水坝的负面效应。如果这是真的,即使因为实际需要而修建水坝,也至少不要为毁坏我们家园而感到骄傲。
最后就是这次中国政府的态度,我得说,主旋律是好的。反应迅速,信息公开。和三十年前的唐山,今年二月的雪灾比,相信大家心里都有数。但是我还是得说,还是不足够。很多国家的救援队不得进入灾区,新闻报道也主要以新华社为主,报道以主旋律为主。虽然说我可以理解这些行为的理由,但是我觉得,我们可以更公开。让我们看到失去生命的人群,失去生命的城市不会让我们感恐慌,一直说没事才会让我们恐慌。让我们看到有发国难财的人,有明哲保身的人,自私的人,也不会让我们止步不前,而是会让我们更明白自己在这种时候怎么做。

2008年5月11日星期日

上帝都反奥,我们怎么办

听说北京地震了,全国地震了,这奥运还办不办?

python的几个改进

首先需要增加的就是kill掉线程的方法,目前我们统统是调用系统函数。有没有搞错阿,需要针对系统写代码不说,还不安全。在线程关闭的过程中没有辗转开解和安全捕获。从最安全的角度上说,要关闭线程最方便的就是给其他线程抛异常。python并非不可以给其他线程抛异常,可非常麻烦不说,具体执行的时候发现,其实根本不是抛异常,而是在执行过程中检查异常。这样当程序在调用外部代码的时候死循环,想kill线程的时候根本不可行。所以安全的关闭线程的异常和直接kill掉线程的方法都要有。
其次,这东西没有什么可以快速辅助处理集合的工具类,例如STL中的set_union等等。虽说每个都不难,可是统一的实现和各自的实现毕竟是有差别的。很多时候,我们只需要抽象的计算两个集合,一个和一个的交集,就OK了。

2008年5月6日星期二

反射的几个类型

所谓反射,其实就是在运行时可以获得代码数据的技术,算是面对对象编程语言的专利。从这个意义上说,反射可以分为三个类型。
头一类是RTTI,其实这根本不算反射,本质上只能说多态。RTTI是一种鉴别某个对象是否为某个类的派生实例的技术,在C++中就有实现。简单的方法就是实现一个特定的虚函数,将当前对象所属的类虚函数表和所属父类的虚函数表一一返回。这样对比某个类的虚函数表,就可以知道是否为派生实例了。支持RTTI,程序才算真正支持了面对对象,而反射则是更高一层的技术。
第二类就是在C#和Java中盛行的反射技术,这种技术的核心在于可以通过名称寻找到对象。例如,我们可以寻找到一个叫做abc的对象,枚举其中的成员和方法,并且执行调用,这才是反射最大的意义。当我们遇到不同的数据输入时,我们可以调用不同的方法来处理这个数据,并且这个过程是动态配置的。而在C++中,我们无法通过编译器支持这个能力,必须手工的建立一个名称和一个对象的关联关系表,在合适的时候通过这个表,获得某个名称的函数入口指针。其实C#和Java中实现的方法和VM息息相关,他们的代码在目标文件中还保持着命名空间-类-对象的结构,Java还进一步的保留了源码(只是被翻译为了更快的P代码),而C#只保留了IL代码。这样VM在执行的时候自然可以很轻松的找到对应的函数,并且获得函数签名。而C类语言的特征是汇编时代的"符号链接"方式,编译的时候保有符号,完成链接就没了。
中间插一句,其实我们完全可以写一个只支持高阶语言的系统。这样的系统未必高效,可一定方便阿。
最后一种则是python中的系统,当用户调用一个类中的函数的时候,使用一个专门的函数来决定调用哪个。因此当对付SOAP这种东西的时候,python可以直接上。而C#,Java,C++都要通过工具生成代理方法。再用代理方法去调用公共函数库,实现调用。因为python直接将调用定向到了一个统一的函数上,所以压根不需要这步。不过这步的代价是严重的性能问题,因为每次函数调用都要去检查调用目标。python是纯脚本语言,占了这点便宜,所以才能这么干。

2008年5月2日星期五

C++继承,虚,转换规则探究

以下讨论的东西都是在VS2005下跑出来的,如果想知道别的编译器规则,请照跑一遍。以下是类定义,函数内容为打印出当前函数名称,所以就不再贴了。
class Base
{
public:
Base();
Base(const Base & o);
virtual ~Base();
virtual Base & operator = (const Base & o);

void function1();
virtual void function2();
void function3();
virtual void function4();
//virtual void function5();
virtual void function6();
};
class Derive : public Base
{
public:
Derive();
Derive(const Derive & o);
virtual ~Derive();
virtual Derive & operator = (const Derive & o);

void function1();
virtual void function2();
virtual void function3();
void function4();
//compiler error
//int function5();
protected:
virtual void function6();
public:
};
首先我们讨论继承下的构造/析构顺序。
pa = dynamic_cast(new Derive ());
delete pa;
Base::Base
Derive::Derive
Derive::~Derive
Base::~Base
关于这段代码多说两句,如果我们把class Derive : public Base中的public删除,就会出现C2243错误,看来默认是私有继承。
先是基类构造,然后是继承类构造。先是继承类析够,然后是基类析够。然后我们将virtual ~Base();的virtual删除,结果就变成了。
Base::Base
Derive::Derive
Base::~Base
注意继承类的析构没了。所以如果你打算让人继承你的类,记得将类的析构改成virtual,否则他怎么写析构都不会被调用的。
然后是虚函数继承。
pa->function1 ();
pa->function2 ();
pa->function3 ();
pa->function4 ();
结果是这样。
Base::function1
Derive::function2
Base::function3
Derive::function4
Derive::function6
看来,虚特性出来不出来完全看基类。注意到上面的function5么?假设你继承了一个类,打算写一个函数,和基类里面的某个虚函数具有一样的名称和参数,但是返回不一样。嘟嘟~~抱歉,编译器错误。而且注意function6,即使在继承类中声明说这是保护函数,也可以通过公开的基类函数的虚特性进行调用。
下面我们要说一下拷贝构造函数,这不可避免的要说到定义。
Derive::Derive(const Derive & o)
{
printf ("Derive::Derive copy constructer\n");
}
猜猜这个会出什么结果?
Base::Base
Derive::Derive copy constructer
要是经常看我blog的人就不会意外,继承类的拷贝构造函数调用的是基类的普通构造函数。如果你打算让基类也拷贝构造,那这么做。
Derive::Derive(const Derive & o):Base (o)
{
printf ("Derive::Derive copy constructer\n");
}
然后是拷贝构造函数的使用时机。运行代码如下,我们逐步分析。
Base ta = *pa;
Base::Base copy constructer
Base::~Base
当对象声明时,如果加一个=,则以=后的对象来构造当前对象,这是拷贝构造的第一个用法。
Derive tb = *static_cast(pa);
Base::Base copy constructer
Derive::Derive copy constructer
Derive::~Derive
Base::~Base
当然,如果我们声明继承类的时候,一样拷贝构造。
//compiler error
//Derive tc = ta;
当我们试图用基类构造继承类的时候,理所当然的,出错了。
void test1 (Base &)
{
printf ("test1\n");
}
test1(*pa);
输出:test1
如果我们以一个对象调用的时候,如果是引用,当然是不拷贝的。
void test2 (Base)
{
printf ("test2\n");
}
test2(*pa);
Base::Base copy constructer
test2
Base::~Base
如果是直接调用,首先是拷贝构造,然后调用,最后析构。
Base& test3 ()
{
printf ("test3\n");
return Base ();
}
pb = &test3();
test3
Base::Base
Base::~Base
当返回对象引用的时候,只有很正常的构造和析构。
Base test4 ()
{
printf ("test4\n");
return Base ();
}
pb = &test4();
test4
Base::Base
Base::~Base
返回对象本身的话,哎,怎么会这样?
熟悉语言的应该看出来了,return Base ();的时候,先跑了一次构造,建立在栈里面,返回的时候要copy到堆中。拷贝构造呢?
这就是传说中的返回构造优化拉,直接构造在堆上面,省掉一次copy,下面我们看看原始的状态。
Base& test5 ()
{
Base b;
printf ("test5\n");
return b;
}
pb = &test5();
Base test6 ()
{
Base b;
printf ("test6\n");
return b;
}
pb = &test6();
Base::Base
test5
Base::~Base
Base::Base
test6
Base::Base copy constructer
Base::~Base
Base::~Base
大家看到了?5的时候先构造,再传回,和返回对象引用的时候行为一致。6的时候可没有返回构造优化,于是先构造,然后拷贝。删除的时候先删除原始对象,再删除拷贝对象,大家可以自行证实这点。
我们再修改上面的调用为下面的。
Base td = test5();
Base::Base
test5
Base::~Base
Base::Base copy constructer
Base::~Base
首先是5的构造,析构,然后才是td的拷贝构造,析构。这个顺序,熟悉语言的人应该感觉到奇怪了吧。按照推论,应当是先拷贝再析构的。如果你这么觉得,还是先看完下面的东西吧。
Base te = test6();
Base::Base
test6
Base::Base copy constructer
Base::~Base
Base::~Base
这才是预计的顺序。注意,这里并没有调用两次拷贝构造。虽然贝壳并不了解机制,不过估计又是一种返回构造优化。
5中例子觉得迷惑的人,不妨在拷贝构造里面打个断点,看看你copy的对象是什么,无效对象!!!!
返回引用的情况下,一旦返回对象的生命周期结束了,返回的数据就无法保证有效。因此返回局部对象是非常危险的,唯一的里外就是3例子中在返回的时候构造一个新的对象而引发的返回构造优化。
下面是拷贝构造和operator =的区别和调用时间。
Base ya = *pa;
Base yb;
yb = *pa;
Base::Base copy constructer
Base::Base
Base::operator =
Base::~Base
Base::~Base
上面一个是拷贝构造,下面一个是普通构造加operator =。
最后是全部的定义和源码,类的定义参考最上面的。
void test1 (Base &)
{
printf ("test1\n");
}
void test2 (Base)
{
printf ("test2\n");
}
Base& test3 ()
{
printf ("test3\n");
return Base ();
}
Base test4 ()
{
printf ("test4\n");
return Base ();
}
Base& test5 ()
{
Base b;
printf ("test5\n");
return b;
}
Base test6 ()
{
Base b;
printf ("test6\n");
return b;
}
int _tmain(int argc, _TCHAR* argv[])
{
Base *pa, *pb;

pa = dynamic_cast(new Derive ());

// test inherit function rule
//pa->function1 ();
//pa->function2 ();
//pa->function3 ();
//pa->function4 ();
//pa->function6 ();

//test copy constructer
//pb = dynamic_cast(new Derive (*static_cast(pa)));
//delete pb;
//Base ta = *pa;
//Derive tb = *static_cast(pa);
//compiler error
//Derive tc = ta;
//test1(*pa);
//test2(*pa);
//pb = &test3();
//pb = &test4();
//pb = &test5();
//pb = &test6();
//Base td = test5();
//Base te = test6();

//diffrence between copy cotr and operator =
//Base ya = *pa;
//Base yb;
//yb = *pa;

delete pa;
return 0;
}

2008年4月24日星期四

无条件的爱国和无聊的自尊

实话说,这个文章不好写,标题更不好起,尤其是在这种爱国情绪异常高涨的敏感时期。不过我还是得说说,关于某些中国人无聊的自尊和奇怪的爱国主义观点。
大家知道,我是个程序员,因为工作的关系,经常得和人讨论一些计算机上的问题。几年前我就和一个人讨论过关于中国是否需要自己的芯片,自己的系统,等等等等的问题。我的意见是,中国不需要自己的芯片产品和系统产品,但是必须拥有自己的芯片技术储备和系统产品储备。因为掌握一个技术系统是一回事情,而投资研发生产就是另外一回了,后者是需要成本的,亏了你负责还是我负责?而对方的观点是,我们必须拥有自己的芯片产品,拥有自己的系统产品,因为我们是中国。关于这个我就很奇怪,英法德意奥这些八国联军,除了美国,也没有多少芯片产品阿(其实是有的,不过多数是专用芯片)。如果说系统,世界上的商用级别系统基本就三个,Windows,MacOSX,Unix,全是美国的。至于要说其他国家开发的系统有没有,一个必须是商用级别的,像我当年自己写的哪种“操作系统”就请免谈,还有一个要和Unix没有派生关系,否则还不是老美的根?按照这两个条件来说,可以说世界上的系统只有美国一家。即使是按照经营公司(注意不是基金会)来算,也是美国大头。至少我只知道一个Novell的SuSE不是美国的,其他商用系统,Microsoft的Windows,Apple的MaxOSX,Redhat,Sun的Solaris,HPUX,IBM的AIX,全是美国的。如果说我们担心技术受制于人,担心后门担心漏洞,而在保密领域采用自主操作系统,并且将这个过程作为国家的操作系统技术储备的过程的话,我是完全理解和支持的。可是自己开发一套芯片产品和系统产品,而且原因仅仅是“我们是中国”。我没有感觉到说话掷地有声的自豪感,反而清晰的感觉到了某些人头上的两个大字“傻X”。
仅仅因为我们是中国,就开发一套自己的系统,因为作为一个大国,不能没有系统。我不知道是不是这个逻辑。这个逻辑看上去到也没错,不过开发以后呢?没人用呢?再号召大家爱国一回,每个人都使用这个国产系统?不说别的,那位兄台当时还在请教我关于Windows安装的某些问题,不知道他会不会因为爱国去弄套银河(据说也是Unix内核),然后装上去写文档。如果我们陷入无条件的爱国中,那就会发生无数傻事,产生无数傻瓜,例如这种号召做操作系统的,还有下面要说的血狮,佳乐福等等。
至于血狮,经历过那个事情的都会觉得,那就是个爱国主义的大笑话。当年血狮打着爱国主义的大旗做了N多宣传,结果呢?一塌糊涂。此后我就一直对所谓的“爱国主义”抱有一定的戒心,因为这个东西实在太容易被人利用了,谁能定义清楚什么是爱国呢?买外国的产品爱不爱国?如果买外国产品就是不爱国,那么东莞为全世界每人生产了一双皮鞋,号称世界工厂的值得我们骄傲的事实,就会变成东莞诱使全世界每人叛国一次了。可能又有人要和我争辩情况不同,可是,不知道大家看没看过公平论。只有当你抛开你是谁的时候,才可能公平。我们暂时抛开中国人的身份,单论买外国货是否就算背叛,那么无论如何是得不到这个结论的。我的观点是,当质量,性价比相近,或者说有的比较的时候,我会选择国货,因为让国内厂商赚钱就是让中国人赚钱,让中国人赚钱就是让我赚钱。但是当国货是个垃圾,或者我很讨厌。我并不介意外国产品,尤其是日本产品。不得不说,虽然我挺讨厌日本的一些东西,然而却不得不佩服日本的另外一些东西。
说到这里,我还准备说一下所谓的“公平论”。我是不知道在哪里看到的简介的,没有拜读过原文,不过我觉得他讲的很有意思。很多时候,我们睁眼说瞎话,就是因为我们是“既得利益者”。只有在你根本不知道你会是谁的时候,你才能真正的说公平。例如一个老板和一个员工,老板希望少发钱多干事,员工希望多发钱少干事。支持谁呢?只有你不知道你会成为谁,你才能公平对待。如果你讲支持老板,可将来做了员工,不要懊悔到心痛么?如果你讲支持员工,偏偏做了老板,不要伤心到白发么?很多事情我们只有抽离开我们是谁,抛弃固有的立场,才能公平对待和评价,当然,前提是你打算做出公平的评价。在这点上,我到是喜欢真小人胜过伪君子。事实是对我有利,我就说,虽然不公平,可世界何尝公平,我就是要这么做,因为对我有利。而不是摆出一堆的道理,站在看似公平的立场上说着冠冕堂皇的东西。北京高考分数线低,要不要改?问我,我说不改,为啥,因为我在北京。
OK,有了上面的一堆爱国和公平,我们现在看看我们今天的重头,佳乐福。这超市也算倒霉,就因为生来带有法国基因,又因为法国政府无聊的支持了下藏独,就被一群群的中国人鄙视。实话说,我觉得实在有不够理智和不够解决问题。首先我们就说说法国政府的言论吧。我一直有一个观点(但是很难做到阿),即使你说的是对的,也应当允许别人说错的,尤其在政治,思想等等这种无法立刻明辩是非的领域。认为自己是正确的,而禁止别人说错误的,我感觉仿佛回到了文化大革命时代。当然,你可以尽力宣传自己的观点,尽力说服对方。但是如果我们坚持我们是对的,而禁止别人说错的,那么就会陷入随大流的怪圈中,你不敢,或者无法说出正确的东西。而我们知道,真理往往掌握在少数人手中。当然,我们会努力表达我们的意见,上层也会(好吧,是至少大多时候会)按照大部分人的意见行为。然而禁止其他人的言论,绝对不是一个好的行为。也许,我的话会被某些人解读为支持藏独,我相信这些人的逻辑一定是零蛋。我说即使我们说的对的,也应当允许别人说错的,那么推论下来的潜台词就是藏独是错的,不过世界上就有些逻辑零蛋的傻瓜。
其次,有人说西藏问题是中国的内政,谢绝国外的讨论和干预。诚然,我支持西藏问题是中国的内政,支持谢绝国外干预。不过评论?我看说这话的人没少对美国指手画脚,至少,他们说西藏问题是西方世界的阴谋。这算不算个悖论?你要说西藏问题谢绝国外评论,就不能说西藏问题是西方世界的阴谋。你要说西藏问题是西方世界遏制中国的阴谋,就不能说谢绝国外评论。否则,你又有什么资格对美国的做法指手画脚大加评论?当然,可能有人会说一个是内政,一个是国际事务。不过,我们就真的没有评论过人家的内政么?如果非洲某个军权国家大肆屠杀贫民,你会闭口不言呢?还是跳出来说话?如果闭口不言,你缺乏人类基本的同情心,如果跳出来说话,凭什么?这不是评论内政么?对于西方的评论,我们只能认为其中有利益集团为了利益而歪曲报道(当然,其中还有中国媒体控制的“功劳”)。然而拒绝国外评论内政,拒绝所有得罪过自己的国家的所有企业……这是否有点小家子气呢?
而后,法国政府支持藏独,关佳乐福什么事情?抛开所谓双赢的理论,抛开所谓是非,如果仅仅因为法国政府支持藏独,佳乐福就要倒霉。那么中国政府干点啥不地道的,是否每个人都要跟着倒霉呢?不要说我们是正义的,伟大的什么狗屁理论,我相信我们是伟大的,然而谁都不能排除政府里有败类(前两天刚审的不就一个,虽然那未必是因败类受审),如果这些人做了什么事情,你是否情愿接受你的倒霉命运?
最后,我们看这次的佳乐福事件,是否觉得有种阴谋的味道?包括将佳乐福设定为反法的典型,包括说佳乐福5.1降价打算让中国人自食其果(原谅我没有使用原文)。佛祖在上(似乎也有点崇洋,要不要改老君?),有多少商家5.1节没有规划降价促销的?这个关于降价的说法一出来,就逼得佳乐福很难做。如果不降价促销,恢复人气,那么可能佳乐福在中国就要受到重创。更不用说竞争对手们一定会趁机降价,甚至打出爱国的招牌。可降价的话,会让大家觉得这个事情真有其事。最可笑的是我收到的一个版本里面,说法国政府斥资让佳乐福降价一成。拜托,以佳乐福现在的状态,降价一成基本只是和当前持平而已(要考虑到不降价的库存成本),最多加上小亏,再加上考虑到将来的人气因素等等,应当是佳乐福主动去做才对。哪里有政府捧钱上门要求降价的说法,何况这笔钱怎么出?法国可不像中国,如果发现有这种钱出来,那基本就是某人或者某些人辞职的结局。我固然不喜欢成为“可能的”法国政府对付中国人民的棋子,但是也讨厌成为某些人对付竞争对手的棋子。
我觉得,我们很多人都陷入了谵妄的爱国主义情绪中。凡是反对中国的,就是反对我的。凡是反对我的,就要被打倒。这种理论反过来用的可以说是疯子也可以说是天才,然而如果正着用只能说是傻瓜。个人不代表国家,国家也只能部分代表个人,其代表的程度和国家的民主程度呈正比。诚然我反对分裂西藏分裂台湾,不过理由说起来可能不怎么冠冕堂皇,因为我有利,其次才是国家有利,西藏人民有利。而且我欢迎别人评论,无论你说好也好,说势利也好,说卖国也好,总之你说你的吧。大国之所以能成为大国,就是要有这么种气度和耐性。如果自尊过剩,别人刚一评论便跳起来让人闭嘴,甚至出口谩骂以致动手(不论谩骂的理由是什么),那最多也就是街头小混混的等级。这种情况下,更像是因为自卑,所以自傲。

2008年4月15日星期二

语言造就人

学汇编的是硬件,学C的是指针,学C++的是模版,学Matlab的是矩阵,学Lisp的是图灵机,学Java的是模型,学Awk的是字符串,学SQL的是数据集。

2008年4月13日星期日

关于上飞机的问题

在blog前,贝壳先问大家一个问题,一个人坐飞机要提前多少时间到机场?
相信我们很多人都坐过飞机,但是很少有人关心过准确的手续时间问题。根据国家的标准,乘客应当在飞机起飞前90分钟到达机场。登机牌是在飞机起飞前90分钟开始办理,直到起飞前30分钟停止办理。机票的停止发票时间也差不多,如果临时到机场,可以在航空公司柜台(票务柜台,不是登机柜台)办理机票,而后在对面的登机柜台直接更换登机牌上飞机,不过这种情况下多数就是全票了。在柜台停止办理登机手续后,紧急柜台(特殊柜台)会开放特殊票(贝壳的特殊折扣票就是这种)。同时,候机室会开放登机口,旅客开始登机。在飞机起飞前5分钟,登机口关闭,辅助系统撤离,等待塔台指令起飞。当然,以上是理想流程。
不过我们中的多数人都不会把上面的时间当回事情,一般情况下,我们会提前一个小时到机场。然后直接拿票,5分钟搞定问题。再直接过安检,10分钟就差不多了。等个5分钟上下,登机口就开放了。上飞机后20分钟,飞机起飞。一切都很不错。但是今早,贝壳遇到了一个诡异的事情,和上次在首都机场跑两次安检,在虹桥机场连等12小时差不多经典。
贝壳今天要乘坐东航的MU5545次航班去烟台(没错,就是那架起飞后机舱内有汽油味返航的,大家可以GG),飞机7:50在虹桥机场起飞,贝壳以前坐过一次。按照前次经验,从家出租过去是18分钟,所以贝壳这次6:20起床,6:30上出租,7:00到,7:50起飞。当然,是这么预计的拉。
实际是,贝壳6:20起床,6:30上的出租,出租到离机场差1公里的时候是6:55,但是这1公里足足走了20分钟。最后贝壳实在等不及,直接下来坐摩托到了候机楼(还花了20大洋)。7:15分到候机楼一看,里外三层的人。贝壳赶紧找保安,"我是急客"。保安说OK,这里外三层全是急客。贝壳没办法,安生排队吧,排到一半,保安说不用排了,肯定关柜台了。贝壳冲到票务柜台,要求改签,结果只有晚上八点的飞机,然后发现两个同事也没办上。这次去6个,只走成一半,有一个7点到的还没排上呢。话说一半,发现对面柜台空了,全冲到东航柜台前面来改签了。据赶上的说,他们是8点上的飞机,安检过的特别慢,上去后空了一堆的位子(完全可以想像)。
如果说过错的话,到说不出东航什么过错,毕竟他们是按照国家规定的时间表走的。可这次早上大规模的滞留,总不会是我们突然都不会坐飞机了吧。也不知道是哪里出了错,这种情况,真的能应对奥运么?

2008年4月7日星期一

python的非经典错误

def comp_tuple_file (tuple_file1, tuple_file2):
for i in tuple_file1:
if i in tuple_file2:
tuple_file1.remove(i);
tuple_file2.remove(i);
if __name__=="__main__":
t1=[(1,"1"),(2,"2"),(3,"3")];
t2=[(1,"1"),(3,"3"),(2,"2"),(4,"2")];
comp_tuple_file (t1, t2);
print t1;
print t2;
错在哪里?
头一次循环,i=(1,"1")被正确移除了。但是接下来,i=(3,"3")?
这个叠代器的行为很有意思哦,貌似叠代器内存储的是集合的索引。
def comp_tuple_file (tuple_file1, tuple_file2):
collection=tuple_file1[:];
for i in collection:
if i in tuple_file2:
tuple_file1.remove(i);
tuple_file2.remove(i);
if __name__=="__main__":
t1=[(1,"1"),(2,"2"),(3,"3")];
t2=[(1,"1"),(3,"3"),(2,"2"),(4,"2")];
comp_tuple_file (t1, t2);
print t1;
print t2;
这才是正确的代码。

链接上的问题

贝壳最近在用库上吃了不少苦头,先是crypto++5.52。编译后怎么也链接不上。后来发现需要用/MT参数编译为多线程。后来又在STLport上又吃一次苦头,可见VC2003的默认单线程模式确实不得人心。
下面说一下STL的编译手记。下载STLport,解压。运行vcvars32.bat设置环境变量,去build/lib下面,运行 configuare -c msvc71(如果你是2003,否则按configuare --help察看你的编译器类型)。然后运行nmake -f msvc.mak install。可以看到有两个目录被建立了,bin和lib。把bin的复制到windows/system32下面,把lib的复制到系统目录下面。 安装就OK了。
上述和boost都差不多,然而和boost不一样的是,编写程序的时候,需要手工指定stlport的头文件路径。boost的可以以<>来引入,因此boost的头可以复制到系统里面去。然而stlport的必须以手工方式指定,否则就要覆盖默认的stl 了。

2008年3月31日星期一

显示自身的代码

void main(){char* a="void main(){char* a=%c%s%c;printf (a,34,a,34);}";printf(a,34,a,34);}
核心是使用printf(a,a)来代换显示,并且用34来规避\转换。当然,完整的要带include,稍微有点区别。

2008年3月26日星期三

说要换blog,但是隔了几个月还是没动,因为贝壳了解到一个悲惨的事实。google提供的blog,是要穿墙的。
这样说的话,我估计这篇blog是没有几个国内用户了。不过华语使用者众,多条路也不错。