基本概念
列表
列表由括号括起来,元素由空格分开。
'(rose violet daisy buttercup)
在LISP中,数据和程序都以同样的方式表示。都是由空格分隔的、由括号括起来的单词、数字或者其他列表的列表。列表是LISP的基础。
原子
原子是LISP里面不可分割的部分。在一个列表中,原子是由空格意义分割的,原子可以紧接着括号。
LISP的一个列表有三中可能的组成方式:
- 括号和括号中由空格分割的原子
- 括号和括号中的其他列表
- 括号和括号中的其他列表及原子
一个没有任何原子的列表就像这样()
,它被称为空列表,可以把它同时看做既是一个原子,也是一个列表。
在LISP的日常使用习惯中,“原子”一词不太常用,LISP编程几乎都是关于列表中的符号的。另外,在LISP中,所有用双引号括起来的文本,包括标点符号和空格,都是单个原子。这种原子被称为string。
求值
当LISP解释器处理一个表达式时,这个动作被称为“求值”。我们称,解释器计算表达式的值。
对一个内部列表求值
如果对一个嵌套在另一个列表中的列表求值,对外部列表求值时可以使用受限对内部列表求值所得的结果。这解释了为什么内存列表总是首先被求值的:因为它们的返回值被首先用于外部表达式。
(+ 2 (+ 3 3))
数字8
就会显示在回显区。
变量
LISP中,可以降一个值赋给一个符号,就像降一个函数定义赋给一个符号那样。但两者含义不同。
- 函数定义时一组指令,这组指令是由计算机执行的。
- 一个符号的值可以是LISP中的任意表达式,如一个符号、一个数字、一个列表或者一个字符串。幼稚的一个符号被称为一个变量。
- 一个符号可以同时具有一个函数定义和一个值。
LISP风格“Hello World"
让REPL打印“Hello World”再简单不过。
CL-USER> "hello world"
"hello world"
其工作原理是,因为字符串和数字一样,带有LISP读取器可以理解的字面语法并且是自求值对象;LISP读取双引号里的字符串,求值的时候在内存里简历一个可以对自身求值的字符串对象,然后再以相同的语法打印出来。双引号本身不是在内存中的字符串对象的一部分——它们只是语法,用来告诉读取器读入一个字符串。而打印器之所以在打印字符串时带上它们,则是因为其试图以奕宗读取器可以理解的相同语法来打印对象。
函数
函数是LISP的基本程序构造单元,可以用类似下面的defun
表达式来定义:
CL-USER> (defun hello-world () (format t "hello world"))
HELLO-WORLD
defun
后面的hello-world
是这个函数的名字。像hello-world
这种用连字符而不是下划线或是内部大写来形成复合词的方法,是标准的LISP风格。
表面上看,这个表达式和你目前见到的所有其他表达式一样,只是另一个被REPL
读取、求值和打印的表达式。这里返回值是你所定义的函数名。但是和format
表达式一样,这个表达式的副作用比返回值更有用。但与format
表达式所不同的是,它的副作用是不可见的:当这个表达式被求值时,一个不带参数且函数体为(format t "hello world")
的新函数会被创建出来并被命名为HELLO-WORLD
。
一旦定义了这个函数,你就可以像这样来调用它:
CL-USER> (hello-world)
hello world
NIL
保存工作结果
在Emacs中可以通过输入C-x C-f
来创建一个新文件,然后根据Emacs的提示输入文件的名字。文件存放的位置并不重要。Common Lisp源文件习惯上带有.lisp
扩展名,尽管有些人用.cl
来代替。
一旦创建了文件,就可以向其中写入之前在REPL里面输入过的定义。需要注意的是,在输入了开括号和defun
以后,在Emacs窗口的底端,slime将会提示它所期待的参数。**具体形式取决于具体Common Lisp实现,但其形式可能如下所示。
(defun name varlist &rest body)
在文件中输入以下内容:
(defun hello-world ()
(format t "hello world"))
可用集中方式将这个定义输入到LISP环境中,最简单的是当光标位于defun
定义内部的任何位置时,输入C-c C-c
,这将启动slime-compile-defun
命令,将当前定义发给LISP进行求值并编译。
输入C-c C-z
切换到REPL中可以尝试调用新函数。
CL-USER> (hello-world)
hello world
NIL
输入C-x C-s
可以启动Emacs命令save-buffer
。
尝试从源文件加载这个函数,着需要退出并重启LISP环境。
执行退出操作可以使用一个slime快捷键:在REPL中输入一个逗号。然后在Emacs窗口底部将提示你输入命令,输入
quit
,然后按回车。这将退出LISP并且关闭所有slime创建的缓冲区。
现在重启slime,使用M-x slime
。
这里有几种方式让LISP知道hello-world
的定义。
- 使用
C-x b
,在其提示时输入hello.lisp
以切换回含有那个文件的缓冲区,然后像之前那样重新编译定义。 - 加载整个文件。
CL-USER> (load "hello.lisp")
T
网友评论