八月 30, 2007

The way I write Perl programs

Perl程序写多了,便形成一定的开发套路。我写程序的习惯一度很不好,没有自动单元测试,没有版本控制,现在这两个陋习正在改掉,尽管做得仍然不够严谨。

我习惯先把程序写出来。在实现一定的功能之前,只是简单地手工测试一下,版本控制也不做。当程序具备一定规模之后,才做版本控制,然后加测试。很多时候都是凭感觉,犹豫便是开始做版本控制的信号,面对自己熟悉的Emacs和Perl,却不敢敲入哪怕一个字符,这是理智在提醒自己,到Subversion上场的时候了。感觉自己在上版本控制前后写程序的风格也不同,之前是大开大阖,之后是谨小慎微,频繁提交。

人以群分,物以类聚。好习惯也一样,做了一个,就想着做另一个。所以版本控制之后,通常便盘算着怎么做自动单元测试。写测试可不像把程序放入版本控制那么简单,总是要费一番脑筋,对程序做较大的改动,才好写出一个个单元测试来。一个容易测试的程序,很有可能是一个容易理解的程序。我看自己做了测试的程序,往往函数体短小,功能单一。

因为写测试很费脑筋(看来还是功力不够啊),所以完成之后容易厌倦,开发出现停滞,需要休息一段时间才能重燃激情。

八月 25, 2007

The power of returning functions

在学习Common Lisp的间隙,禁不住手痒,写了两个Perl脚本,第一次尝试将函数作为返回值,感觉相当美妙。其实,返回一个函数并不是什么神奇的法术,在C或C++这样的语言里,我们可以返回一个函数指针,该指针指向程序里的某个函数;然而在Perl里面,作为返回值的函数,不需要是已经存在的函数,我们可以动态地创建一个函数,将其返回。

要理解这里面的不同,首先要理解什么是closure。

sub make_prompt {
    my $n = 0;
    return sub {
        ++$n;
        "shell[$n]>";
    }
}

函数make_prompt返回一个匿名函数,该匿名函数的返回值是个shell提示符,每次执行该函数得到的shell提示符都是不同的,类似于CPAN shell,提示符里含有一个数字,表示提示符出现的次数。

my $prompt = make_prompt();
my $p1 = $prompt->(); # shell[1]>
my $p2 = $prompt->(); # shell[2]>

理解函数make_prompt的关键是其局部变量$n在该函数返回后仍然存在,或者更准确地说,那段空间仍然存在,只是$n不再与其绑定(binding)在一起。换句话说,make_prompt返回的不仅仅是个函数,还包括定义这个函数时与其相关的那部分环境(此例中为$n的空间),使得返回函数可以正确执行。

需要注意的是,每次调用make_prompt时的$n是不同的,所以返回的函数之间没有任何依赖关系

my $prompt1 = make_prompt();
my $prompt2 = make_prompt();
my $p11 = $prompt1->(); # shell[1]>
my $p12 = $prompt1->(); # shell[2]>
my $p21 = $prompt2->(); # shell[1]>

可以看出,closure具有某种程度的封装功能。

理解了closure之后,可以学习currying了。我们稍微修改一下make_prompt的定义:

sub make_prompt {
    my $n = shift;
    return sub {
        my $shell = shift;
        my $m = $n++;
        "$shell[$m]>";
    }
}
my $prompt1 = make_prompt(1);
my $promtp2 = make_prompt(100);
my $p11 = $prompt1->("shell"); # shell[1]>
my $p21 = $prompt2->("root"); # root[100]>

现在,通过传给make_prompt不同的参数,其返回的函数的行为也不一样了,第一个从1开始,第二个从100开始。而且通过让返回函数也接受参数,使得shell提示符可以非常个性化。

八月 11, 2007

Should we congratulate or not?

公司里似乎有个不成文的规矩,结婚了发喜糖,生小孩发喜蛋,有些同事甚至发给不认识的人,面对这样的情形,我会礼节性地道一声“恭喜”。同学的关系更亲密些,会在宝宝生出来之后发个短信,报母子平安几斤几两什么的,我也一律回复“恭喜”二字。不过,每次恭喜的时候,都不禁暗自嘀咕:“等我有了孩子的时候,也会这么做吗?”应该不会。当年结婚的时候,喜糖也只发给几个熟识的同事。

这些想法也就是一闪而过,没有过多地去思考,直到刚刚读了The Most Excellent and Lamentable Tragedy of Richard Stallmanreddit.com上的评论。说实话,一开始我确实对Richard Stallman的反应感到很吃惊,在读了正反两方的评论之后,仍然无法接受这种如此直接地表达自己人生观世界观的方式,但是我不再感到惊讶,只有这样的人,才可能开创自由软件运动,才可能坦然地说出:

I am sorry to hear it.

以及

It helps more people, too.

老子说过:

天地不仁,以万物为刍狗,圣人不仁,以百姓为刍狗。

——解释见《被中国人误传了数千年的七句话》中的第七句。

在Richard Stallman心里,千千万万的Emacs用户,要比一个亲密战友的新生女儿重要。只是这样的博爱,不仅一般人做不到,也难以接受,而要做这类人的朋友,恐怕要更难些。

老外们对此的评论很有意思,什么人口问题啊环境污染啊都上来了,看得我这个友邦人士要莫名惊诧了。

八月 09, 2007

My working environment is better

很庆幸,到目前为止,我在工作中还没碰到结党营私,也没有老兵欺负新兵的现象,跟他们比起来,我的工作(软)环境简直太好了。工作间隙,同事们可以凑在一起聊聊楼市、股市、或者电影什么的,上网不受限制,我还抽空学习Common Lisp。

说到Lisp,Practical Common Lisp最后几章不打算看了。后面几章都是程序实例,看了一部分,感觉再这样看下去也不会有太大的收获。这种感觉就像当初看完《Learning Perl》之后仍不知道怎么写一个Perl程序一样,有一点点挫折感。其实在学习另外几种语言时也有同样的问题,唯一的不同是我坚持用Perl写程序,也不管当时写出的东西有多烂。

SLIME加Emacs是我准备用来写Lisp程序的环境,感觉SLIME挺新奇的,第一次通过如此交互的方式来学一种语言,希望可以更快地上手。

学习过程中的挫折感只要坚持一下就过去了,工作氛围不好就不是一件容易解决的问题了,我原先也抱怨过这不好那不好,但是时间久了,心态放平和后,感觉自由度、人性化都是有的,只是没那么理想化罢了。尤其是对比一些凄惨的例子之后,感觉应该充分利用、倍加珍惜工作氛围中有利的一面,可别“少壮不努力,老大徒伤悲”。

八月 03, 2007

Programming language war

争论要有理有据,尤其是说别人不好的时候(这年头大家都爱听好话),别像微软那样老是FUD来FUD去的。程序设计语言之间的比较往往很难做到准确公平,然而比较各种语言之间的优劣却是很多人最感兴趣的事。说自己中意的语言好无可厚非,却偏偏要夹带着对其它语言的攻击、贬低,甚至动不动就宣称某某语言已日薄西山,马上就完蛋了。这不,有人终于受不了了,因为

It seems every day I am questioned about why I write in Perl versus PHP, Java, C#, Ruby, Python, or [insert your favorite language here]. People say things like, “Perl isn’t used anymore is it?”

我尝试学过多种语言,多数都半途而废了,但从没因自己学习吃力而贬低过哪种程序设计语言,所以无法理解为什么有些人会这样做,尤其是将一些很主观的话用客观的方式说出来,比如,不说“我不喜欢ABC”,而要说“ABC的存在毫无意义”;不说“我认为XYZ”,却直接说“XYZ”,仿彿事情真的就是那样。

也许不是所有人都是有意识这么说的,但我确实很反感这种貌似言之凿凿却偏偏毫无根据的所谓“断言”,最近就因此发飙过一次

八月 02, 2007

Tough question

看了Mark Dominus的Tough questions,不禁回想起自己的一段经历。毕业找工作是件大事,所以定下来之后逢人便讲。一般来讲,对方在听到公司名字之后,会追问一句:“具体是做什么的啊?”

“开发编译器”,我答道。

外行人会接着问:“什么是编译器啊?”

我尝试着用不同的方式回答,没有人听懂过。最夸张的一次是在火车上,有人搭讪,不知怎么又跑到这个问题上,我们三个同事轮番讲解,对方始终目光迷茫。

看来问题是否难以回答,不仅取决于问题本身,还要看回答的对象是谁。