[置顶] 屠夫与大夫
一个刚做了爸爸的屠夫气愤地说。“简直是疯了!加上产科医生的诊费、住院费和护理费,这个孩子竟然要800块钱1 公斤!”
可能很多人看到这个笑话时,都在哈哈大笑,嘲笑屠夫的职业病。但是又有多少程序员想过我们自己或许正在做和这个屠夫相同的事呢?
屠夫每天拿着一把大刀剁肉,心中就只有刀。
我们每天拿个xxyy的编程语言写程序,是不是我们心中就只有xxyy了呢?
我们的思维会不会被xxyy所奴获了呢?
现在不少的公司使用考一些比较古怪的语法来测试应聘者是否对某种编程语言足够熟悉。似乎招聘方和应聘者都以此为乐,市面上类似的书籍也层出不穷。
但是,我们使用编程语言,是希望能够用编程语言清楚的反映我们想要做到的事情,让他乖乖的听话,简单的完成我们想达到的目的。而不是需要我们端个小板凳儿,坐在他们旁边,听听他们想要我们做些什么。
正如《松本行弘的程序世界》里所说,计算机是人类的奴隶,那岂有奴隶骑在主人头上来的道理?
每一种语言反映了语言发明人的思维和思考,而语言又会指导语言的使用者如何思考。
以Lisp为例,LISP是LISt Processing的缩写。list就是lisp的核心。
(list 1 2 3)即返回一个由(1 2 3)组成的链表
(car (list 1 2 3))返回的是链表中第一个元素,这里是1
(cdr (list 1 2 3))返回的则是从第二个元素开始到最后一个元素结尾所组成的链表,这里是(2 3)
当我初次接触到lisp的时候,我很好奇一个问题,为什么lisp中的list两个最基本的方法是car和cdr?
为什么不像java那样有一个get(int index)的接口,返回list中的某个index位置的元素呢?
Q:如果我真的想知道list中的第2个元素呢?
A:cadr
Q:第3个元素呢?
A::caddr
Q:第100个元素呢?
A:你在玩儿我吗?
鄙人愚见,因为lisp的发明者,根本就不希望你那样去写程序。lisp的发明人希望你尽可能的去用car和cdr实现你的程序逻辑。这是为什么呢?
考虑一个反转list的函数
一种可能的java实现,充分的使用了java中的get:
public List reverse_list(List list) {
int first = 0, last = list.size() - 1;
while (first < last) {
Object firstObject = list.get(first);
Object lastObject = list.get(last);
list.set(first, lastObject);
list.set(last, firstObject);
first++;
last--;
}
return list;
}
如果是lisp呢?能写出个类似的实现吗?有点儿困难,那lisp是怎么写的呢?
(defun reverse-list(lst)
(if (null lst)
nil
(append (reverse-list (cdr lst)) (list (car lst)))))
上面的lisp程序是递归构成的。
当list是空的时候,什么反转都不需要做,就返回一个空nil;当list不为空,则用car取出最前面的一个元素放在最后面,再把cdr的reverse的结果放在前面。这样就构成了一个递归得到的list反转实现。
正是因为这样,lisp里没有get,使用car和cdr就足够了。
对于其他语言的程序员来说,没有get或许是个噩梦,但在lisp中car和cdr会激发你使用递归编程的愿望。
递归就是把某一问题的当前输入规模的结果用该问题的更小输入规模的结果表达。
使用cdr可以得到一个小一点儿的list,处理cdr就得到一个更小输入规模的结果。
(reverse-list (cdr lst))
使用car获得现在需要处理的部分,衔接好cdr和car就会得到当前输入规模的结果。
(append (reverse-list (cdr lst)) (list (car lst)))
当达到最小规模的输入的时候,结果是显而易见的。就像上面list为空的情况,返回一个nil就行。
从另外一个角度说,递归描述了问题的解和该问题的子问题的解之间的相似性。lisp使用car和cdr提供了这样一个工具,让你轻松的完成递归。
Paul Graham是一个坚定的Lisp支持者。在《黑客与画家》一书中,Paul Graham经常使用这样的一种思考方法,如果你认为前人的某件事或者某种选择做的很sb,那么也很有可能你现在做的这件事和做的这个选择会让后人觉得很sb。可以看出,Paul Graham已经习惯了使用递归的方法思考问题。
我们每天都在写程序,或者一周中的五天在写,在这样强度的练习下,我们的思维能力必定会被我们的程序语言所左右。慢慢的,不好的程序语言会奴隶我们的思想。
我听过几次学校里面公司的演讲,一次是《clean code》,另一次是《重构》。每次有不同的人,或一次或多次的问这么一个问题:使用小方法难道不会额外增加程序的调用开销吗?这样程序的效率不久下降了吗?但我想问,谁见过有程序因为调用另外一个函数而导致性能太差最后崩掉的?
当然在结果上来说,任何编程语言都是图灵等价的,但是不同的语言似乎会给你不同的人生,让你拥有不同的思考。
有的语言让你变成了屠夫,而有的让你变成了大夫。