最近在看一本书《Land OF Lisp》,看了大部分。离前一次看 Lisp 方面的书刚好三年,用 Emacs 也有四年了,这期间接触的多是简单的 Elisp。总得来说,Lisp 的书看起来是比较有趣的,这本也不错,稍微比《Practical Lisp》简单。竟然有个第 6.5 章,Lambda 这么重要,怎么说也要占一章!第八章实现了一个小游戏。Wumpus(Hunt the wumpus) 是个古老的游戏,那个年代还没有绚丽画面,只有文字界面。这里游戏的规则是:
地图为一个无向图,玩家控制一个人物在图中行走,目的是寻找潜伏在节点中的一个怪兽。其中要边走边推理,得出怪兽在哪个节点。
还有其他角色,有的节点隐藏着超级蝙蝠,它能把你扔到图的任何位置。节点之间的边可能有警察。
如果你推测出怪兽的位置,向那里射箭,如果射中则胜利,否则输掉。如果你不小心从有警察的边通过了,也死掉。
在怪兽的附近两个距离范围内,会有血气。如果一个点的某条边有警察,这个点会有光晕。
说起来复杂,来看副图。有点像挖地雷那种小游戏。有?符号的为没访问过的点,*为当前点,从 14 到 15 遇到警察死掉了。
上周末玩了好几个小时,还挺难胜,主要还是图比较大,游戏一开是整个地图是已经生成了的,要偷懒可以看看。
来看看如何用 Lisp 代码来实现这个程序,程序比较短。首先是如何生成图,需要生成一个随机的连通图。设定节点数目和边的数目,以编号代表节点。random-node:随机地选一个节点。edge-pair:连接两个点表示边。make-edge-list: 重复 N 次,生成 N 条边的集合。这个随机图可能不是连通的,下面的代码找出孤立的点集,用一些边连接起来这些孤立的点集,随机图产生完成。第二步向某些点之间加警察,随机的。这其中用了各种 mapcar 和 Lambda,这样的效果使得 Lisp 程序看起来全是括号。mapcar 的意思就是我要在这个列表上面所有的元素上都执行这个 Lambda 函数。visited 列表保存已经访问过的节点,know-city-nodes 更新 (不是纯函数式编程的风格),know-city-edges 根据访问的节点,生成已知的路径,当前已知的用 dot 画出来。graphviz是个好东西,最近也在学习用这个来画一些流程图,效果挺好的。
乱说说 Common Lisp,看了一些这方面的资料,这语言不管有多少牛逼人士簇拥 (最近 Paul Grahamd 的书被翻译了),使用的人还是太少还是有一定的历史原因,早期的实现效率是一个问题,而当实现和硬件都不错了的时候 C/C++ 已经成大局了。另一个很重要的原因是,文档不是很好,我想找个处理图片方面的库,见到一个 README 文件跟救命稻草似的,打开一看”Do you really need DOCS?”。Lisp 的哲学是语言不能给太多限制,甚至做到代码就是数据、数据就是代码,你可以轻而地为语言添加特性,你还可以用宏来写出生成代码的代码。Lisp 给了程序员最大的自由来挑战语言的限制,所以会出现如此多种的方言。好的一面是面对特定的问题或许能得到优美而高效率的解决方法,而这个代码对于另外一个程序员来说太难读懂 (特别是夹杂了宏的代码),继而难于流传。这里有篇经典的Lisp:Good news,Bad news,作者为早期用 Lisp 作为开发语言开公司的。
以后看看 Haskell 吧,这个比较有前途。