Random Tech Thoughts

The title above is not random

Using Infix Notation in Common Lisp

以前在用 Common Lisp 做计算方法的作业的时候就觉得 Common Lisp 的 prefix notation 对数学表达式不够友好。最近打算用 Common Lisp 实现些算法,再次遇到了这个问题。好在已经有人做了将 infix notation 转成 prefix notation 的工作,所以我就可以用现成的了。

CMU 的 AI repository 里有一个 INFIX: Infix reader macro for Common Lisp,实现了一个 reader macro,用它就可以在 Common Lisp 里面使用 infix notation 了,不过需要把表达式用 #I() 括起来。

代码注释里面有一些例子,也有使用说明,看看就能用了。这里举几个简单的例子:

1
2
3
4
#I(1 + 2) ; 3
#I(2^^2 + 4) ; 8
#I(1 == 2) ; 判断, 得到 nil
#I(1

有了这个 read macro 数学表达式的输入就简单多了,布尔表达式也可以用类似 C 的语法来写,不用再做人肉 infix –> prefix 转换器了。

除此以外,它还提供了函数调用数组访问的特殊语法,而且用 = 号做赋值,同样是用了类似 C 的语法。

1
2
3
4
(defparameter v (vector 0 1 2 3))
#I(v[1]) ; 1
#I(v[0+1] = -1) ; 现在 vec 中的第二个元素值为 -1
#I(random(5)) ; 调用函数 random

我比较喜欢用这种语法来访问数组元素,比用 aref 看起来更清楚些,还能少打些字。

如果想知道这个 read macro 在背后究竟做了什么的话可以在 #I() 之前加个单引号。

1
'#I(v[0+1] = -1) ; (SETF (AREF VEC (+ 0 1)) 1)

跟 Python, Ruby 这样的语言比起来 Common Lisp 的程序代码并不是很紧凑,因为有很多长的函数名,一些常用的操作也没有特殊的语法来支持。只使用 prefix notation 的好处就是可以方便的使用宏,更容易的进行元编程,也使得程序员可以自己定义语法,这里的 infix read macro 就是一个例子。

Paul Graham 在他的 Lisp 方言 Arc 中就希望能够使 Lisp 的代码更紧凑,更短,添加一些语法来简化某些操作。通常来说程序中的 bug 数量与程序的长短成正比,与使用的语言无关,如果能写出来的程序更短那么错误应该也会更少。不知道以后 Arc 的发展会如何,不过现在就已经有一个对 Lisp 语法做出许多改动的基于 Common Lisp 的语言,Qi。Qi 的作者正准备大幅改进语法,推出 Qi II,等出来了再去看看,到时候看是否可以把它和 Common Lisp 混合使用。

Comments