数据结构简介

ELisp 程序的基本结构和其他 C/C++ 等常见的编程语言有很大的不 同。 一个 ELisp 程序就是一串 Elisp 的 expression ,程序的运 行就是对 expression 进行 evaluate 。expression 是由一些基本 的 ELisp Object 组成的。

Lisp 的数据类型

一个 Lisp object 是一个可以用 Lisp 程序处理的数据。 type 就 是一个 object 的集合。这一点很重要,type 是一个集合,一个 object 的集合。 说一个 object 的类型是某一个 type ,就是表示 这个 object 是这个集合中的一个元素。有的 object 可能属于多个 type 。有的 type 可能是其他 type 的交集或者并集。

每一个 object 至少属于一种 type 。一个 object 也可以属于多于 一种 type 。

Emacs 内部有几种基本的 object type ,所有其他的 type 都是由 这些 type 构造出来的。所以他们叫做 primitive type 。 每一个 object 属于一种 primitive type ,也只能属于一种primitive type。 从集合的观点来来说,任何两个 primitive type 的交集都 是空集。 primitive type 包括 integer, float, cons, symbol, string , vector, hash-table, subr 和 byte-code function 。还 有一些 primitive type 是和编辑功能十分相关的,例如 buffer, window, frame, overlay 等等。

每一个 primitive type 都有一个对应的 Lisp 函数,用于判断一个 object 是否属于某一个 primitive type 。

值得注意的是, 和很多其他语言不同, Lisp 的 object 是 self-typing 的,也就是说,object 内部隐含着一个 primitive type ,用于表示 object 属于哪一种 primitive type 。 例如,如 果一个 object 是一个 vector ,那么没有任何办法把它看成一个 number 。 Lisp 知道他是一个 vector ,不是一个 number 。

在很多语言中,例如 C ,程序员必须为每一个变量声明一个数据类 型,编译器知道这个类型,而变量本身并不包含任何类型信息,一旦 编译结束,在运行时刻,我们无法通过变量本身得到任何类型信息。 但是 Emacs Lisp 完全不是这样,一个 Lisp 变量的 值可以是任何 type ,变量本身除了纪录变量的值 ,也记录了值的类型。

Printed Representation 和 Read Syntax

一个 object 的 printed representation 就是 Lisp 解释器产 生 的输出格式。 Read syntax 就是 Lisp 解释器可以接受的输入格式。

大多数 object 可以有不止一种 read syntax 。 有一些 object 可 能没有 read syntax ,因为这些对象是不能够有代码表示出来。例 如 buffer, frame, window, window configuration, frame configuation 等等。除了这些没有 read syntax 的 object 以外, 任何 object 的 printed representation 也是这个 object 的read syntax 。

在其他语言中,只能用程序代码来表示一个 expression 。 在 Lisp 中,一个 expression 本质上是一个 Lisp object 。 而表示object 的程序代码只是一个 object 的 read syntax 。 通常,没有必要强 调这种区别,但是你必须知道这一点,否则有的时候你会被稿晕头的。

每一个 type 都有一个 printed representation ,包括那些没有 read syntax 的 type 。这些没有 read syntax 的 object 使用井 号表示法来表示 printed representation ,就是说,用 `` #< '' 开头,用 ''>'' 结尾,中间包含一段描述性的文字。这种表达方式 不是一个 object 的 read syntax ,是不可以表示一个object 的。 例如

(current-buffer)
=> #<buffer objects.texi>

对一个表达式求值时, Lisp 解释器读取源代码,然后根据 read syntax 生成一个 object , 然后对这个 object 求值。 尽管如此, 求值和读取一个对象是两个独立的过程。根据 read syntax 来读取 出来的 object 并可能放到以后再对他求值,也可能根本就不求值。 例如 :

(if nil (some-function-not-exits 1 2) 1)
=> 1

在这里尽管 some-function-not-exits 可能不存在,但也不会产生 错误,因为 (some-function-not-exits 1 2) 这个 list object 尽 管已经产生了,但是没有对他求值。

注释

用分号开头,到这一行结尾之间的都是注释。注释是不会被执行的, Lisp 解释器当作看不见注释一样。他的主要目的是为了增加程序的 可读性。 当然,字符串常量中的分号不会当作注释的开头。

有一些编程习惯值得我们注意。好的编程风格十分重要。

单个分号开头的注释

这个注释会在源代码的结尾对其。通常简单的解释一行代码。

双分号开头的注释

这种注释会和下面的代码对齐缩进,解释对后面有相同缩进排版的多 行源代码.

如何这种注视出现在函数之外,这种注释解释后面的出现的函数。一 般情况下,每一个函数都会有 docstring ,但是有些函数只对一个 package 内部有用,没有必要包含 docstring ,那么就需要一些这 种注释,为了 package 的开发者和维护者阅读的注释。

三个分号开头的注释

这种注释会对齐在最左边。

四个分号开头的注释

用于很长的注释当中,表示段落开头。