Common Lisp 要点总结
Lisp 知识点汇总

编译器

Common Lisp是Lisp的一种有名的方言, 另外两种是Scheme和Clojure。此外还有一种是Emacs Lisp。

终端开发环境

交互式环境

进入:$sbcl 或者 $clisp
退出:(exit) 或者 (quit)

非交互式环境

$ sbcl --non-interactive --load  源文件
或者
$ clisp 源文件

第三方GUI库

CAPI and Lispworks 提供了GUI库,可以在多个平台上提供可视化程序。

http://www.lispworks.com/products/capi.html

https://lailalife.wordpress.com/2010/11/26/my-first-hello-world-program-using-lispworks/

语言特性

什么是S表达式?

基本操作符

小括号

quote

有quote就是数据,没quote就是程序。它的缩写是’。

quote 操作符(又记作’)接受一个实参,并完封不动地返回它,而不求里面的值。而是将其当做符号。

‘(+ 3 5) 得到的就是(+ 3 5),而不是8。

quote ‘将小写变为大写

atom

(atom x)当x是一个atom或者空的list时返回原子t,否则返回NIL。

null?

eq

(eq x y)当x和y指向相同的对象的时候返回t,否则返回NIL。

car

(car x)要求x是一个表,它返回x中的第一个元素。

cdr

(cdr x)同样要求x是一个表,它返回x中除第一个元素之外的所有元素组成的表。

cons

(cons x y)返回一个cons cell(x y),如果y不是一个list,将会以dotted pair形式展现这个cons cell。

cond

(cond (p1 e1) …(pn en))的求值规则如下.对“条件表达式p”依次求值直到有一个返回t.

如果能找到这样的p表达式,相应的“结果表达式e”的值作为整个cond表达式的返回值。

remove

接受一个对象和一个列表,返回不含这个对象的新列表,而不是改变原来的表。

progn

(progn () () () …) progn 接受任意数量的表达式,依序求值,并返回最后一个表达式的值。

apply

apply 接受一个函数和实参列表,并返回把传入函数应用在实参列表的结果。

function

把函数的名字传给 function ,它会返回相关联的对象。它的缩写是#’。

eql

判断两个对象是否是同一个对象。

equal

判断两个对象是否相等。

and/or

逻辑运算,具有和其他语言一样的特点。或运算具有短路特性,左边开始只要有一个为真就不再继续判断。

数据类型

数值是有类型的,变量是没有类型的。或者说任何变量都是列表。

内置类型

fixnum 、 integer 、 rational 、 real 、 number 、 atom 和 t 类型。

真和假

用原子t表示真,而用空表 () 或NIL表示假。

列表

list

(list 1 2 3 )

最常见形式的列表。

property list (plist)

(list :a 1 :b 2 :c 3)

有下标的list,像不像map呢?

GETF, which takes a plist and a symbol and returns the value in the plist following the symbol

变量

创建局部变量

(let ((变量1赋值)(变量2赋值)…) (作用域))

变量只在整个let括号区域内有效,出来之后就失效了,因此这些都是局部变量。

创建全局变量

(defparameter *名称* 值)

通常全局变量的名字都是以*开头、以*结尾的。

另外使用下面的setf也可以创建全局变量。

(defvar *名称* 值)

它也可以创建全局变量,并且只是第一次管用。

变量赋值

(setf 变量名 新值)

如果变量名是以前没出现过的符号,那么就认为是定义了一个新的全局变量。setf 的第一个实参几乎可以是任何引用到特定位置的表达式。

注意,赋值传递的是引用。变量名实际上是引用(指针指向真实数据)。也就是说通过setf复制得到的两个对象肯定是eql的。

语句

条件分支语句

(if ()
     ()
     ()      ;else可以缺省
)

拷贝语句

copy-list

接受一个列表,然后返回此列表的复本。返回的复本并不是原对象的引用了,而是创建了内容相同的新对象。

迭代语句

do

参数一是初始表达式 参数二是迭代结束表达式 参数三是循环

(do
     ( (variable initial update) (variable initial update) )
     ()
     ()
)

dolist

直接遍历列表的每个元素 (dolist (临时变量 列表名) (主体) ) 迭代时临时变量会是列表中的每一个元素。

函数

函数体的定义

defun

(defun 函数名 (参数)
     (car (cdr (cdr x))))

输入参数:括号括起来,即使没有参数也要有括号。

返回值:可以是最后一个表达式的值。如果没有返回值的话,需要用return。

函数的调用

(函数名 参数)

调用无参数的函数时不要有空括号了。

lambda

((lambda (x) (+ x 100)) 1)

输出函数

format

(format t “~A plus ~A equals ~A. ~%” 2 3 (+ 2 3))

参数1是输出位置、参数2是输出模板、参数3之后是输出对象。其中输出位置默认t时是标准输出,输出模板里~A 表示被填入的位置,而 ~% 表示一个换行。

FORCE-OUTPUT is necessary in some implementations to ensure that Lisp doesn’t wait for a newline before it prints the prompt.

print

(print 1)

输入函数

read

没有参数时是标准输入。它解析它所读入的东西,并返回产生出来的 Lisp 对象。

read-line

Reads from input-stream a line of text that is terminated by a newline or end of file.

y-or-n-p

(y-or-n-p “Question[y/n]: “)

输入y返回T,输入n返回nil

功能函数

(PARSE-INTEGER “字符串”)

这个函数会尝试把字符串转换为数字,如果转换不了,就会抛出异常signal an error.

参数junk-allowed可以让这个函数在转换不了时不抛异常,而是返回0.

(parse-integer (prompt-read “Rating”) :junk-allowed t)

Lisp语言中的9个设计思想

1960年,John McCarthy发表了一篇论文,给定一些简单的操作和函数声明,你可以构建一套编程语言。他的核心理解是使用简单的列表结构来表达编码和数据,他把这套语言称作是Lisp

Lisp中存在9个重要的设计思想,这9个思想在50年多年后的今天看起来仍然那么先进。

(1) 条件结构

if-then-else结构, 现在大家都觉得这是理所当然的,但是当年Fortran I就没有这个结构,它只有基于底层机器指令的goto结构。

(2) 函数也是一种数据类型

在Lisp语言中,函数与整数或字符串一样,也属于数据类型的一种。它有自己的字面表示形式(literal representation),能够存储在变量中,也能当作参数传递。一种数据类型应该有的功能,它都有。

(3) 递归

Lisp是第一种支持递归函数的高级语言。

(4) 变量的动态类型

在Lisp语言中,所有变量实际上都是指针,所指向的值有类型之分,而变量本身没有。复制变量就相当于复制指针,而不是复制它们指向的数据。

(5) 垃圾回收机制

Lisp是第一种提出和实现垃圾回收机制的语言。找寻并标记所有的Lsip对象,栈上的所有符号对应的值、数据、函数认为是可达的,经由可达符号达到的符号也是可达的。其他的符号认为是不可达的。垃圾回收期将不可达的符号回收到free list里以供再利用。

(6) 程序由表达式组成

Lisp程序是一些表达式树的集合,每个表达式都返回一个值。这与Fortran和大多数后来的语言都截然不同,它们的程序由表达式和语句组成。

区分表达式和语句在Fortran I中是很自然的,因为它不支持语句嵌套。所以,如果你需要用数学式子计算一个值,那就只有用表达式返回这个值,没有其他语法结构可用,否则就无法处理这个值。

后来,新的编程语言支持块结构,这种限制当然也就不存在了。但是为时已晚,表达式和语句的区分已经根深蒂固。它从Fortran扩散到Algol语言,接着又扩散到它们两者的后继语言。

(7) 符号类型

符号实际上是一种指针,指向存储在散列表中的字符串。所以,比较两个符号是否相等,只要看它们的指针是否一样就行了,不用逐个字符地比较。

(8) 符号树表示代码

代码使用符号和常量组成的树形表示法。

(9) 语言的持久可用

无论什么时候,整个语言都是可用的。Lisp并不真正区分读取期、编译期和运行期。你可以在读取期编译或运行代码,也可以在编译期读取或运行代码,还可以在运行期读取或者编译代码。

原文链接

Garbage Collection

The Little Schemer

The Seasoned Schemer

*****
Written by Lu.dev on 16 August 2015