Common Lisp之小试~
看了《黑客与画家》,对Lisp语言比较好奇,于是就弄了本《Lisp使用教程》,有空的时候就学习一下。Lisp的实现有很多,我用的Emacs+sbcl+slime,因为网上资料比较好找。跟着的前几章体验了一下,先记录一下:
在slime中,你会看到
CL-USER>
敲入命令,只要合乎Lisp语法,便会马上得到执行。
在Lisp中,这个过程叫做read-eval-print loop(REPL)。
有时候又被叫做top-level,top-level listener,Lisp listener。
举个简单的例子:
CL-USER> 10
10
第一个10是用户输入,第二个则是slime打印的值。
这个过程是这样的:首先reader(即REPL里的R)读取用户输入的10并创建一个表示这个值的Lisp对象。
然后evaluator(R)对其求值,得到10,(在Lisp中数值是自求值对象,意味着对它求值得到本身)。
最后这个值交给printer打印出来。
这个例子还不够过瘾的话,再来一个:
CL-USER>(+ 2 3)
5
这个又是怎么求值的呢?
其实在Lisp中,任何在括号中的东西都被看成是list。(不要忘了lisp名字的由来~),在上面的情况下,
是一个由+,2,3 这3个元素组成的list。通常情况下Lisp对list求值时将第一个元素看成是函数名,
余下的元素被看成是要被求值的函数参数。因此上面的过程就是:+意味着一个加法函数,2和3被读取并求值,
然后传递给加法函数,返回5。最后5被传递给printer打印。
好吧。。。Lisp还有别的办法对表达式求值,这里只是一个简单的介绍。
----------------------------------------终于来到了Lisp的“Hello World”程序--------------------------------------------------------
没有“Hello World”程序的编程书是不完整的,所以“Hello World”来了。
CL-USER>"hello, world"
"hello, world"
上面便是该程序的输入和输出。和数值一样字符串也是自求值变量,所以上面的过程跟输入10,打印10类似。
要注意的是:双引号并不是字符串的一部分,它之所以在输入输出都出现是因为,输入时它作为提示reader
读取字符串的语法,输出时则是因为printer会以reader能理解的同样语法输出。
这样一讲,似乎上面的例子不算是一个program,而是一个value,那就来真的吧。
CL-USER>(format t "hello, world")
hello, world
NIL
上面的例子中,format是一个函数,跟C语言的printf有点像,不要深究,倒是那个NIL需要注意下。
哪来的NIL呢?NIL是Lisp版本的false/NULL,在这里它是format表达式的返回值。只是这里format函数
的副作用比返回值更有用。C语言中其实也一样的,printf不也有返回值嘛,只不过被丢弃了而已。
其实这也不算一个完整的程序,不过也快要达到我们的最终目的了,你已经有了“hello world”的所有部件,
现在所有要做的事情就是把他们打包进一个函数。(这正是Lisp的美妙之处,边改进边运行以达到你最终想要的效果)
切入正题,我们要的那个函数如下:
CL-USER>(defun hello-world () (format t "hello, world"))
HELLO-WORLD
这个程序就更有意思了,打印的值居然是HELLO-WORLD?那这个神秘的HELLO-WORLD是怎么来的呢?另外它还向我们初步
展示了怎样去定义个函数。那么就看看究竟发生了什么!
先看函数定义,跟其它语言很类似,hello-world是函数名,括号里是参数(这里不需要参数),在后面是函数体;
然后到了神秘的HELLO-WORLD,它跟我们的函数名不是一样的吗,换个大写而已嘛!
其实,HELLO-WORLD就是我们定义的函数。当我们输入上面的表达式时,名为HELLO-WORLD的函数被创建了,然后表达式返回
了这个函数的名字,和上面的format表达式一样,这个表达式的副作用也比返回值要有用的多-创建了HELLO-WORLD,所以
接下来就可以直接调用这个函数了
CL-USER>(hello-world)
hello, world
NIL
它和我们直接输入format表达式的结果是一样的,包括最后的NIL,因为Common Lisp中的函数总是返回最后一个被求值的表达式的值。
可能钻牛角尖的人还是认为它不是一个“程序”,因为一旦退出了lisp,这个函数就消失了,再进入lisp之后自然就不可以用了,
这个问题很好解决,把你的函数定义写入文件,然后从文件加载函数不就好了?至于如何在.lisp文件中定义函数,然后加载文件就
不详细介绍了~