GNU Emacs Lisp Manual 学习笔记
目录
- 约定
- Lisp 数据类型(data type)
- Printed Representation 和 Read Syntax
- comments
- programming types
- Editing Types
- Read Syntax for Circular Objects
- Type Predicates
- Equality Predicates
- Numbers
- Strings and Characters
- Evaluation
- Control Structures
约定
nil 和 t
在 Emacs lisp 中,一个 symbol nil 在三种不同的意思
- 一个名为 nil 的 symbol
- 布尔值为 false
- 一个空 list ,即0个元素的 list
当作为 variable 时(即第一种意思),它的 value 永远是 nil 。
对于 Lisp 而言, ()
和 nil
相同的,它们都代表相同的 object —— symbol nil
在程序中,我们用 ()
来强调它是一个空 list ,用 nil 来强调它是一个布尔值: false
t 代表布尔值: true
在 Emacs Lisp 中, nil 和 t 是特殊的 symbol —— 它们总是 evaluate 他们自身。因此,你不需要在程序员用引号引着他们来作为常量。如果试图修改它们的值的话,会导致一个 setting-constant 错误。
booleanp object 函数 —— 如果 object 是 t 或 nil 之一,则会返回非 nil 。
(booleanp '(hell world)) => 返回 nil (booleanp t) => 返回 t (booleanp nil) => 返回 t (booleanp "hello world") => 返回 nil
evaluate
form
一个可以进行 evaluate 的 expression 就称为一个 form.
Lisp object
evaluate 一个 form 总是会产生一个结果,这就是 Lisp object
Function 相关
&optional 表示 argument 是可选的。 &rest 表示可以有任意个 argument 。但它必须跟在一个 argument 后面。这些 argument 会被接收放到一个 list 里面。
Lisp 数据类型(data type)
每一个 object 至少属于一种 type 。相同 type 的 objects 拥有相似的结构,并且通常用在相同的 context 中。 type 可以 overlap ,并且 object 可以属于两种或多种类型。 因此,我们可以问:一个 object 是否属于特定的 type ,但不能问 object 是什么 type 。
primitive type ,它是 Emacs 内建的 type ,是构成其他 type 的基础。 每一个 object 属于且仅属于一种 primitive type ,这些 type 包括:
- integer
- float
- cons
- symbol
- string
- vector
- hash-table
- subr
- byte-code
- function
- buffer
每一种 primitive type 都有相应的 Lisp function 来检测一个 object 的 type 是否属于它们的一员。
Lisp 不同于其他的语言,它的 objects 是 self-typing 的: primitive type 的每个 object 就是指它自身。举个例子,如果一个 object 是一个 vector ,没有任何东西可以把它当作是 number ,Lisp 知道它是一个 vector ,而不是一个 number 。
在大多数语言中,程序员必须声明每个 variable 的 data type ,并且 type 是由 compiler 所知的,并不在 data 中表示。这样子的声明,在 Emacs Lisp 中是不存在的。一个 Lisp 的 variable 可以是任意 type 的 value ,并且它会记住你所保存的 value 、 type 以及所有东西。
实际上,仅有一小部分的 Emacs Lisp variable 可以确定它的 type 的 values 。
Printed Representation 和 Read Syntax
一个 obejct 的 printed representation ,它是一个由 Lisp printer (function prin1 ) 为该 object 产生的格式化的输出。每一个 data type 都有一个唯一的 printed representation 。
一个 object 的 read syntax ,它是一个由 Lisp reader (function read )为该 object 产生的格式化的输入。这并不必是唯一的。
在大部分情况下,一个 object 的 printed representation 也是它的 read syntax 。然而,有一些 types 并没有 read syntax ,因为在 Lisp 程序中,将这些 types 作为常量输入是没有意义的。这些 objects 使用 hash notation 来进行 print ,它是由字符 #<
、一个描述性字符串(典型地是 type name ,后接 object name ) 和一个关闭的 >
。例如:
(current-buffer) ⇒ #<buffer objects.texi>
Hash notation 是不可读取的,因此 Lisp reader 无论在什么时候遇到 #<
时都会发出错误信息 invalid-read-syntax 。
在其他语言中,一个 expression 就是 text ,没有其他的 form 了。在 Lisp 中, 一个 expression ,首先它是一个 Lisp object ,其次才是 text ,它是 object 的 read syntax 。通常情况下,是不需要强调这种区别的,但你必须在脑海里一直记住它,否则你偶尔会感到非常迷惑。
当你 evaluate 一个 expression 时,Lisp interpreter 首先读取它的 textual representation ,产生一个 Lisp object ,然后 evaluate 这个 object 。然而, evaluation 和 reading 是分开的两个动作的。 Reading 则返回读取的 text 所表示的 Lisp Object ; 之后,这个 Object 可以或不会进行 evaluate 。
comments
以一个 ; 号开始的,是程序的注释,它会一直到行尾。(只要不是在字符串常量用的字符串或字符,都会是一个 comment)
programming types
Emacs Lisp 中通常有2大目录的类型:一种是 Lisp 编程相关的,一种是与编辑相关的。前者存在于一些 Lisp 的实现中。后者是 Emacs Lisp 独有的。
integer type
它的范围取决于机器。最小的范围是 -536,870,912 ~ 536,870,911 (30 bits ,例如 : -229 ~ 229-1) ,但一些机器提供更大的范围。Emacs Lisp 算术 function 并不会检测一个 integer 是否 overflow 的。因此 (1+ 536870911)
的结果是 -536,870,912 ,如果 Emacs 的 integer 是 30bits 的话。
它的 read syntax 是一个数字 sequence (10进制),可带有一个可选的在开头的符号(正负)以及一个可选的在结尾的 period 。 它的 printed representation , 由 Lisp interpreter 产生的, 是不会带有 '+' 或 结尾的 '.' 的。
-1 ; The integer -1. 1 ; The integer 1. 1. ; Also the integer 1. +1 ; Also the integer 1.
作为一个特殊的例外,如果一个 integer 的数字 sequence 是太大或太小,以及是一个 invalid 的 integer object ,则 Lisp reader 会将它读取为一个 floating-point number 。例如,如果 Emacs integer 是 30bits 的话,则 536870912
会读取为一个 floating-point number 536870912.0
floating-point type
它等同于计算机中的 scientific notation ,你可以想象 floating-point number 是一个分数与一个以10为底数一起的。精度以及指数的范围是依赖于机器的,Emacs 使用 C 语言中的 double 来保存该 value ,并且内部是使用以2为底数而不是以10为底数的。
它的 printed representation 要求是要有一个浮点(并且至少要带有一位小数),或者指数,或者两者都有。例如
‘1500.0’, ‘+15e2’, ‘15.0e+2’, ‘+1500000e-3’, and ‘.15e4’
character type
Emacs Lisp 中, character 只是一个 integer 。换句话说, character 是用它们的 character code 来表示的。例如 character A
是用 integer 65 来表示的。
单独的 character 只是偶尔在程序中使用,更多的是使用 strings ,它是由 character 组成的 sequence 。
在 strings 和 buffers 中的 characters code 的当前限制的范围是 0 ~ 4194303
. Code 0 ~ 127 是 ASCII code ,其余的是非 ASCII 。
基本的 char syntax
由于 characters 本质上是 integers ,所以它的 printed representation 是一个十进制数 (decimal number) 。这个也可以作为 charater 的 read syntax ,但用这种方式来写的话,在 Lisp 程序中并不够清晰。你应该总是使用特定的 Emacs Lisp 提供的为 charater 所使用的 read syntax 。这些 syntax 是以一个 question mark 开头的。
通常的字母 charater 的 read syntax 是一个 question mark 后接该 charater 。因此 ?A
为 charater A, ?B
为 charater B 等。比如
?Q ⇒ 81 ?q ⇒ 113
你可以为 punctuation character 使用同样的 syntax ,不过通常添加一个 \
是一个好的主意,这样的话 Emacs commands 为那些正在编辑的 Lisp code 就不会困惑了。例如 ?\(
是写一个开括号的方式。如果这个 character 是 \
,则你必须使用第二个 \
来引住它: ?\\
下面是一些特殊字符:
?\a ⇒ 7 ; control-g, C-g ?\b ⇒ 8 ; backspace, BS, C-h ?\t ⇒ 9 ; tab, TAB, C-i ?\n ⇒ 10 ; newline, C-j ?\v ⇒ 11 ; vertical tab, C-k ?\f ⇒ 12 ; formfeed character, C-l ?\r ⇒ 13 ; carriage return, RET, C-m ?\e ⇒ 27 ; escape character, ESC, C-[ ?\s ⇒ 32 ; space character, SPC ?\\ ⇒ 92 ; backslash character, \ ?\d ⇒ 127 ; delete character, DEL
这些以 backslash 开头的 sequences 就是 escape sequences , 因为 backslash 扮演了一个 escape character 的角色,这与字符 ESC
没有任何关系。在 character constant 或 string constant 中的 \s
仅仅是写一个 space (空格)。
在任何一个没有特定 escape 意义的 character 前面加上一个 backslash 是允许的,并且也没有任何害处的。因此 ?\+
等同于 ?+
。在大部分的 character 前面没有理由为它们添加一个 backslash 。然而,你应该在以下这些字符之一之前添加一个 backslash : ()\|;'`"#.,
以免 Emacs commands 对这些编辑中的 Lisp code 感到困惑。
你也可以在 whitespace character 前添加一个 backslash ,例如 : space, tab, newline 和 formfeed 。不过,使用 \t
或 \s
来代替 whitespace character 如 space, tab 则更清晰可读。
如果你在一个 space 前加上 backslash ,则后面应该还有一个额外的 space 来区分它。
一般的 Escape Syntax
Emacs 提供了几种 escape syntax 来使用非 ASCII 文本 character 。
- 你可以使用指定 character 的 Unicode value 。
?\unnnn
表示一个指定 unicode code point 为U+nnnn
的 character,nnnn
是一个四位数的 hexadecimal number (十六进制)。如果你的 code point 是高于U+ffff
的话,要使用一种不同的 syntax 来指示:?\U00nnnnnn
, 它表示 code point 为U+nnnnnn
,这里的nnnnnn
是一个六位数的 hexadecimal number 。*Unicode Standard* 仅定义了 code point 的值到U+10ffffff
,因此,如果你指定的 code point 比它大,Emacs 会发送一个错误信号。 - 你可以指明该字符的 hexadecimal character codes 。一个 hexadecimal escape sequences 由一个 backslash ,
x
,以及 hexadecimal character code 组成。因此?\x41
表示 characterA
,?\x1
表示字符C-a
等。 - 你可以用 octal 来指明 character 的 character code 。一个 octal escape sequences 由一个 backslash 后接三个 octal 数字组成。因此
?\101
表示 characterA
。这种方式仅可以表示到 octal code 为 777 的字符集。
它们也可以用在 strings 中。
Control-Character Syntax
Control character 可以使用另一种 read syntax 来表示。它由一个 question mark 后接一个 backslash , caret 和一个相关的非 control 字符 ,不管是大写还是小写。例如: ?\^I
和 ?\^i
都是 character C-i
的有效 read syntax ,它的 value 是 9 。
你可以使用 C-
来代替 ^
。因此 ?C-i
等同于 ?\^I
和 ?\^i
。在 strings 和 buffers 中仅允许出现在 ASCII 中的 characters 。
由于历史原因,Emacs 将 DEL
字符看作等同到 ?
的 control character 。即:
?\^? ⇒ 127 ?\C-? ⇒ 127
因此,当前并不能表示字符 Control-?
Meta-Character Syntax
一个 meta character 是通过 META 修饰键来输入的。 代表这样一个 character有 2^27
位所表示的整数个。我们使用 high bits 来表示这个 META ,其他的 bits 表示基础的 character code 范围。
在 string 中, 附加到 ASCII character 的 2^7
位表示 meta character 。因此,meta character 在 string 中适应的 code 范围为 128 ~ 255
, 这些都是普通的 ASCII character 中的 meta 版本 characters 。
它的 read syntax 为 \M-
。例如 ?\M-a
表示 M-a
,你可以用 \M-
与 octal character code 一起,或与 \C-
或其他任何的 character syntax 一起。因此,你可以写 M-A
为 ?\M-A
或 ?\M-\101
。
其他的 Character Modifier Bits
一个 graphic character 是通过它的 character code 来指示的。例如,ASCII 区分 character a
和 A
。但是 ASCII 没有办法来表示一个 control character 到底是 upper case 还是 lower case 。 Emacs 使用 2^25
位来输入 Control character 指示 shift key 。这可能仅在你使用 X terminal 和 其他特别的 terminal 才有区别。Lisp 中对于 shift bits 的 syntax 为 \S-
。因此, ?\C-\S-o
或 ?\C-\C-O
都表示 shift-control-o character 。
X Window System 定义了其他三种 modifier bits :
- hyper ,
\H-
- super ,
\s-
- alt ,
\A-
因此, ?\H-\M-\A-x
表示 Alt-Hyper-Meta-x
注意,\s 后面没有 '-' 的表示是 space character
symbol type
在 GNU Emacs Lisp 中,一个 symbol 就是一个带有 name 的 object 。symbol name 作为它的 printed representation 。一个 symbol 的 name 是唯一的,没有两个 symbol 会有相同的 name 。
一个 symbol 可以作为 variable 或 function 的 name ,或者拥有一个 property list 。或者它仅作为区分其他所有 Lisp objects ,以便它在数据结构中可以容易识别出来。在一个给定的 Context 中,仅会使用这些中的一个意思。但是,你可以用 symbol 独立地来处理这所有的方式。
一个以 colon (':') 开头的 symbol name 称为 keyword symbol 。这些 symbol 自动地表现为 constants ,并且通常下仅用几个具体的选项来与一个 unknown symbol 来进行比较。一个 symbol name 可以包含任何字符。绝大部分情况下,使用 letters, digits 和 punctuation charater '-+=*/' 来进行书写。这些 names 的要求是没有特殊的 punctuation ; name 的 charater 要足够长以便让它看起来不像是一个 number 即可 (如果还像 number 的话,那就 name 前面加上一个 \
来强制解释为 symbol) 。 _~!@$%^&:<>{}?
这些 charater 虽然比较少使用,但也要求是没有特殊的 punctuation 。
其他的 charater 要用在 symbol name 的话,则需要进行 escape 。
下面是一些例子:
foo ; A symbol named ‘foo’. FOO ; A symbol named ‘FOO’, different from ‘foo’. 1+ ; A symbol named ‘1+’ ; (not ‘+1’, which is an integer). \+1 ; A symbol named ‘+1’ ; (not a very readable name). \(*\ 1\ 2\) ; A symbol named ‘(* 1 2)’ (a worse name). +-*/_~!@$%^&=:<>{} ; A symbol named ‘+-*/_~!@$%^&=:<>{}’. ; These characters need not be escaped.
Sequence types
一个 sequence 表示一个带有元素顺序的 set 的 Lisp object 。在 Emacs Lisp 中有两种:
- lists
- arrays
Lists 是最常用的 sequence 。一个 list 可以拥有任意 type 的 elements ,它的 length 可以容易地通过添加或移除 elements 来改变。
Arrays 是一个 fixed-length 的 sequences 。它进一步分为:
- strings ,它的 elements 必须为 characters 。这些 characters 可以有 text properties ,就像在 buffer 中的 characters 一样。
- vectors , 它的 elements 可以是任意的 type 。它不支持 text properties ,即使它的 elements 是 characters 。
- char-tables ,与 vectors 类似,但它是通过任意有效的 character code 来索引的。
- bool-vectors ,它的 elements 必须是 t 或 nil
Lists, strings 和其他 array type 都有相似性。例如,它们都有一个 length /
,并且所有的 elements 都是从 0
到 /-1
的索引 。
通常,同一个 sequences 是不可能读取两次的,因为 sequences 总是在读取时创建一个新的。如果你读取 sequences 的 read syntax 两次的话,你会获得两个拥有相同内容的 sequences 。有一个是例外的:空list ()
,它总是表示同样的 obejct nil 。
Cons Cell 和 List Types
一个 cons cell 是一个由2个 slots 组成的对象:
- CAR slot
- CDR slot
每一个 slot 可以持有任意的 Lisp object 。我们也可以这样说,这个 cons cell 的 CAR 是当前拥有的任何 object ,CDR 同样也如此。
一个 list 是由一系列的 cons cell 链接在一起的,使得每个 cons cell 的 CDR slot 持有下一个 cons cell 或是一个空 list 。一个空 list 实际上是 symbol nil 。由于绝大多数的 cons cell 是用作 list 的部分,我们指那些由 cons cell 组成的结构称为 list 结构 。
C 语言程序员注意: 一个 Lisp list 因此表现得像由 cons cell 为节点的 linked list 。因为 pointer 在 Lisp 中是隐式的,我们不区分一个 cons cell 是持有一个 value ,还是 point to 这个 value
由于 cons cell 是 Lisp 的核心,我们为这些 object 有另一个单词而不是 cons cell 来称呼它,称为 atoms
list 的 read syntax 和 printed representation 是相同的:由一个左括号,一个任意数是的 elements ,以及一个右括号组成。例如:
(A 2 "A") ; A list of three elements. () ; A list of no elements (the empty list). nil ; A list of no elements (the empty list). ("A ()") ; A list of one element: the string "A ()". (A ()) ; A list of two elements: A and the empty list. (A nil) ; Equivalent to the previous. ((A B C)) ; A list of one element ; (which is a list of three elements).
在括号中的每一个 object ,都会成为 list 的 element 。也就是说,是一个 cons cell 组成了每个 element 。*cons cell* 的 CAR slot 持有 element ,并且它的 CDR slot 指向 list 中的下一个 cons cell element 。最后一个 cons cell 的 CDR 是设置为 nil 。
CAR 和 CDR 的名字,是从 Lisp 的历史中获得的。原始的 Lisp 实现运行在一台 IBM 704 计算机上的,它将 words 分成两部分: address 和 decrement 。 CAR 是一个从 register 保存的 address 指示的地方获取内容的指令; CDR 是一个获取剩余内容的指令。
Drawing Lists as Box Diagrams
(rose violet buttercup) => --- --- --- --- --- --- | | |--> | | |--> | | |--> nil --- --- --- --- --- --- | | | | | | --> rose --> violet --> buttercup (rose violet buttercup) => --------------- ---------------- ------------------- | car | cdr | | car | cdr | | car | cdr | | rose | o-------->| violet | o-------->| buttercup | nil | | | | | | | | | | --------------- ---------------- ------------------- (A ()) => --- --- --- --- | | |--> | | |--> nil --- --- --- --- | | | | --> A --> nil ((pine needles) oak maple) => --- --- --- --- --- --- | | |--> | | |--> | | |--> nil --- --- --- --- --- --- | | | | | | | --> oak --> maple | | --- --- --- --- --> | | |--> | | |--> nil --- --- --- --- | | | | --> pine --> needles -------------- -------------- -------------- | car | cdr | | car | cdr | | car | cdr | | o | o------->| oak | o------->| maple | nil | | | | | | | | | | | -- | --------- -------------- -------------- | | | -------------- ---------------- | | car | cdr | | car | cdr | ------>| pine | o------->| needles | nil | | | | | | | -------------- ----------------
Dotted Pair Notation
Dotted Pair Notation 它是为 cons cell 显式地表示 CAR 和 CDR 的 syntax 。这种 syntax 里, (a . b)
表示一个 cons cell ,它的 CAR 部分是 object a
,它的 CDR 部分是 object b
。 Dotted Pair Natation 比 list syntax 更能用,因为它的 CDR 并不必是一个 list 。不过,在 CDR 部分也是 list 时,它比 list syntax 更笨拙点 。在 Dotted Pair Notation 中 list (1 2 3)
要写成 (1 . (2 . (3 . nil)))
。
当进行打印时,*Dotted Pair Notation* 仅在 cons cell 的 CDR 不是一个 list 才会使用。
(rose . violet) => --- --- | | |--> violet --- --- | | --> rose (rose violet . buttercup) 等同于 (rose . (violet . buttercup)) => --- --- --- --- | | |--> | | |--> buttercup --- --- --- --- | | | | --> rose --> violet
Association List Type
一个 association list 或 alist 是一个特殊结构的 list ,它的 elements 是多个 cons cell 。在每一个 elements 中, CAR 视作为一个 key , CDR 则视作是 associated value (在一些情况下,associated value 是保存在 CDR 的 CAR 部分)。它通常用作 stacks ,因为它可以容易地添加或删除 list 前面的 association 。例如:
(setq alist-of-colors '((rose . red) (lily . white) (buttercup . yellow)))
第一个 element 中, rose
就是 key , red
就是 value 。
Array type
参考上面的 sequences type
String type
在 Lisp 中, evaluate 一个 string 会返回它自身。
string 的 syntax
它的 read syntax 是: 一个 double-quote ,一个任意数是的 characters,以及另一个 double-quote : "like this"
。为了包括一个 double-quote ,可以在它前面加上一个 backslash 。即: "\""
,这个 string 仅包含一个 double-quote character 。你可以包含一个 backslash ,通过在它的前面加上另一个 backslash 。比如这样: "this \\ is a single embedded backslash"
newline character 在 read syntax 中并不是特殊 character 。如果你在 double-quote 之间写了个 newline ,它会成为 string 的一部分。但如果你 escaped newline —— 即在前面加上 \
的话,则不会成为 string 的一部分。例如, Lisp reader 在读取一个 string 时会忽略一个 escaped newline 。它也会忽略通过 \
来 escaped space character 。例如:
"It is useful to include newlines in documentation strings, but the newline is \ ignored if escaped." ⇒ "It is useful to include newlines in documentation strings, but the newline is ignored if escaped."
Non-ASCII Characters in Strings
在 Emacs strings 中有两种表示 non-ASCII 文本:
- multibyte
- unibyte
粗略地说, unibyte string 保存了 raw bytes ,而 multibyte string 保存为 human-readable 文本。在 unibyte string 中的每一个 character 是一个 byte ,即它的 value 是 0~255
之间。相比之下,在 multibyte 中的每个 character 允许的 value 为 0~4194303
之间。这两种情况下,所有大于 127 的都是 non-ASCII 。
你可以在一个 string 中包含一个 non-ASCII character ,通过直接书写它的字面即可。如果一个 string constant 是从一个 multibyte source 例如 multibyte buffer , multibyte string, 或通过 multibyte 来访问的 file 的话,之后 Emacs 每次读取的 non-ASCII character 会自动成为 multibyte string 的。如果它是从一个 unibyte source 读取的话,那么它也会自动成为 unibyte 的。
书写一个 character 为 multibyte string 的替代方式,你可以用一个 escaped sequence 来写它的 character code 。
如果你写的是任意的 Unicode-style 的 escaped sequence \uNNNN
或 \U00NNNN
,Emacs 会自动假设它是 multibyte 的。
你也可以使用 hexadecimal escape sequence (\xn)
和 octal escape sequences (\n)
。但要注意,如果是这些形式的话,所有这些 escape sequences 会成为 unibyte (即小于 256) 并且没有其他的 non-ASCII 字面量 character 或 Unicode-style escape sequences 。这就是说,它会假设所有 non-ASCII character 都会是 8-bit 的 raw bytes 。
Nonprinting Characters in Strings
你同样也可以使用 backslash 来在 string 中 escape-sequences 一个 character literal 。(但不要用 question mark 在一个 character constant 开头。)
例如,你可以在 string 中写一个 nonprinting character : tab ,像这样子: "\t"
。
然而,并不是所有的 characters 你都可以通过 backslash escape-sequences 来书写的。仅是在 ASCII 的 control character 下的才可以。*strings* 并不会区别 ASCII 中 character 的大小写。
正确地说, strings 并不能持有 meta character ,但当一个 string 用作一个 key sequence 时,这里提供了一个约定来表示 ASCII character 的 meta 版本。如果它是用在 string constant 中来指示 meta character ,则会将它的 27 位设置到 string 中。 如果是用在 define-key
或 lookup-key
,会被转换为相应的 numeric code 。
strings 并不能持有 hyper , super 或 alt 这些 modifier character 。
Text Properties in Strings
syntax :
#("characters" property-data...)
property-data 由0个或多个 elements 元素组成,以下三个为一组:
beg end plist
beg 和 end 都是 integer ,它们一起指示了 string 的范围。 plist 是对这些范围的 string 的 property list 。例如:
#("foo bar" 0 3 (face bold) 3 4 nil 4 7 (face italic))
没有指定范围的,默认就是没有 properties 的。
Vector Type
它是一个可包含任意 type 的一维 array 。在 vector 中,访问任意一个 element 的时间一样的(在 list 中,则时间并不一样,最开始的 element 耗时短)
它的 printed representation 是由: 一个左边的 square bracket , elements 和一个右边的 square bracket 。这也是它的 read syntax 。例如:
[1 "two" (three)] ; A vector of three elements. ⇒ [1 "two" (three)]
Char-Table Type
它是一个一维 array ,可包含任意 type 的 elements ,通过 character code 来进行 index 。
它的 printed representation 像 vector 一样,除了它有一个额外的 #^
开头。
Bool-Vector Type
它也是一个一维 array ,elements 必须是 t 或 nil
bool-vector 实际指定的内容是在 string constant 用 bitmap 表示的。在 string 中的每个 character 包含 8bits ,它指示了 bool-vector 的之后的 8 个 elements (1 代表 t ,0 代表 *nil*国).
它的 printed representation 像一个 string ,除它了是以 #&
开头,且后面接着它的 length 。
(make-bool-vector 3 t) ⇒ #&3"^G" (make-bool-vector 3 nil) ⇒ #&3"^@"
因为它是三个 t 即是: 111 ,所以,它显示为 C-g (C-g 的 Code 就是 111)。下面的同理。
Hash Table Type
它是一个非常快的查找表,与 alist 相似,但是它更快。它的 printed representation 是 properties 和 contents 。比如这样:
(make-hash-table) ⇒ #s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8 data ())
Function Type
它是 executable code ,就像其他语言中的 function 一样。在 Lisp 中,不同于大多数其他语言, function 也是 Lisp Object 。一个 non-compiled 的 function 是一个 lambda expression ,也就是说,它的第一个 element 是 symbol lambda 。
在大多数语言中,一个 function 是不能没有 name 的。在 Lisp 中,一个 function 没有真正的 name 。一个 lambda expression 可以作为 function 来调用,即使它没有 name ;为了强调这点,我们也称它为 anonymous function 。一个 带有 name 的 function 仅是一个带有有效 function 的 symbol 。
大部分的时候,在 Lisp 程序中,是用它们的 name 来调用的。然而,你也可以构建或在 run time 获取一个 function 然后通过 primitive function funcall
和 apply
来调用它。
Macro Type
一个 Lisp macro 是 user-defined 的结构,用来扩展 Lisp language 的。它的表现与 function 很相似,但是有不同的 argument-passing 。一个 Lisp macro 的 form 是一个 list ,它的第一个 element 是 symbol macro 以及 CDR 部分是一个 lisp function object ,包括 lambda symbol 。
Lisp macro 通过用内建的 defmacro 来定义。
注意,Lisp macro 和 keyboard macros 是完全不同的两个东西。当我们使用 macro 时,如果没有特别说明,是指 Lisp macro ,而不是 keyboard macro
Primitive Function Type
一个 primitive function 是一个可以从 Lisp 调用的 function, 但是它是用 C 语言写的。它也称为 subrs 或 build-in functions 。( subr 源于 subroutine )
大部分的 primitive function 当它们被调用时,会将它的们 arguments 全进行 evaluate 。一个没有将它们的 arguments 全进行 evaluate 的,称为 special form 。
对于 caller ,调用一个 function 不需要关心它是否是一个 primitive function 。但是,如果你试图用 Lisp 来重新定义一个 primitive function 时,这就需要关心了。因为 primitive function 可能从 C 代码中直接调用。 用 Lisp 重定义的 function 在调用时会使用重定义后的 function ,但从 C 代码中调用的,仍会使用 build-in 定义的。因此,我们不鼓励重定义 primitive function
术语 functions 是指所有的 Emacs functions ,不管它是用 Lisp 还是 C 写的。
它没有 read syntax ,并且它是用 带有 subroutine name 的 hash notation 来 print 的。如:
(symbol-function 'car) ; Access the function cell ; of the symbol. ⇒ #<subr car> (subrp (symbol-function 'car)) ; Is this a primitive function? ⇒ t ; Yes.
Byte-Code Function Type
Byte-Code Function objects 是由 byte-compiling 后的 Lisp code 生成的。在内部,一个 byte-code function 与 vector 很像;然而,*evaluator* 在 function call 时会特别处理这种 data type。
它的 printed representation 和 read syntax 类似 vector ,但在 [
前面有个额外的 #
。
Autoload Type
它是一个 list ,但它的第一个 element 是 symbol autoload 。它保存了 symbol 的 function definition ,作为一个 real definition 的 placeholder 。一个 autoload object 是说,*real definition* 当需要的时候,从一个带有 Lisp code 的文件中进行 load 。它包含了文件的 name 和一些关于 real definition 的其他信息。
当该文件已经 loaded 的时候,该 symbol 就有了一个新的 function definition ,并且不再是一个 autoload object 了。
Finalizer Type
一个 finalizer object 在不再需要这些对象的时候帮助 Lisp code 来清理它们。一个 finalizer 持有一个 Lisp function 。当一个 finalizer object 在 garbage collection 进行后, 变成 unreadable ,Emacs 就会调用它相关的 function 了。该 function 在每个 finalizer object 中仅会执行一次。
Editing Types
上一部分是大多数 Lisp dialects 中通用的 type 。这一部分的 type 是 Emacs Lisp 提供与 editing 相关的 type 。
Buffer Type
一个 buffer 是一个持有可被编辑的文本的 object 。大部分的 buffer 是持有 disk 文件的内容的,但也有一些 buffer 是用作其他目的的。大部分的 buffer 也是对用户可见的,但也有一些是不可见的。 它没有 read syntax 。它的 printed representation 是用 hash notation :
(current-buffer) ⇒ #<buffer objects.texi>
Marker Type
一个 marker 是批一个特定的 buffer 中的一个 position . 因此 ,它有两部分:一部分是指一个 buffer ,另一部分是指一个 position 。 它没有 read syntax 。它的 printed representation 是用 hash notation ,给出当前 charater 的 position 以及 buffer 的name 。
(point-marker) ⇒ #<marker at 10779 in objects.texi>
Window Type
一个 windows 是 Emacs 用来显示一个 buffer 的 terminal screen 的一部分。每一个 window 都有一个与之相关的 buffer ,它的内容就出现在 window 中。相比之下,一个给定的 buffer ,可能出现在一个 window ,或没有 window 或在好几个 window 中。
虽然可以同时存在好个 window ,但在同一时间,只有一个 window 是 selected window ,即 cursor 出现所在的 window 。它对应的 buffer ,也就称为 curent-buffer 。
在screen 中 Window 分组为 frame 。每一个 window 仅属于一个 frame 。
它没有 read syntax 。它的 printed representation 是用 hash notation ,给出 window number ,以及显示的 buffer 的name。比如:
(selected-window) ⇒ #<window 1 on objects.texi>
Frame Type
一个 frame 是包含一个或多个 Emacs window 的 screen area 。
它没有 read syntax 。它的 printed representation 是用 hash notation ,给出了 frame 的 title ,加上它的 address 。(用于唯一地标识一个 frame)。如:
(selected-frame) ⇒ #<frame emacs@psilocin.gnu.org 0xdac80>
Terminal Type
一个 terminal ,可以显示一个或多个 Emacs frame 。 它没有 read syntax 。它的 printed representation 是给出 terminal 的序号以及它的 TTY 设置文件名。如:
(get-device-terminal nil) ⇒ #<terminal 1 on /dev/tty>
Window Configuration Type
一个 window configuration 保存了在一个 frame 中的 window 的 position, size 以及 content 的信息。
它没有 read syntax ,它的 printed representation 看起来像 #<window-configuration>
.
Frame Configuration Type
一个 frame configuration 保存了在所有 frames 中的关于 window 的 position, size 和 content 的信息。它不是一个 primitive type ,实际是一个 list ,它的 CAR 是 frame-configuration ,CDR 是一个 alist 。
Process Type
process 通常意味着 running program 。Emacs 自身在这种类型中执行的。然而,在 Emacs Lisp 中,一个 process 是一个 Lisp object ,是设计用来通过 Emacs process 来创建一个 subprocess 的。例如 shell, GDB 和 compiler 这些程序,在 Emacs 中是执行在 subprocesses 中的。它没有 read syntax ,它的 printed representation 是:
(process-list) ⇒ (#<process shell>)
Stream Type
一个 stream 是一个可用作 character source 的 object 。一些不同的 type 可以使用这种方式: markers, buffers, strings, 和 functions 。 nil object ,除了其他含义,也可作用 stream 。它表示 standard-input 或 standard-output 的 value 。 t 也可以作为用在 minibuffer 或 echo area 的 input stream 。
它没有 read syntax ,它的 printed representation 是与它的 primitive type 是什么就会显示为什么。
Keymap Type
一个 keymap 是映射 key type 为用户指定的 commands 。它实际上是一个 list ,它的 CAR 是 symbol keymap 。
Overlay Type
一个 overlay 是应用到一部分的 buffer 的 specifies properties 。用于临时用一个不同的显示风格来显示buffer中的一部分。
Font Type
一个 font 指示了在一个 Graphical terminal 中如何显示文本。实际上有三种不同的 font types:
- font objects
- font specs
- font entities
每一种都有稍微不同的 properties 。它们都没有 read syntax 。它们的 printed representation 分别为 :
#<font-object> #<font-spec> #<font-entity>
Read Syntax for Circular Objects
你可以在一个 object 前面用 '#n=' label , 之后在其他地方使用 '#n#' 来引用同样一个 object 。 n 是 integer 。例如:
(#1=(a) b #1#)
Type Predicates
Emacs Lisp 并不进行 type checking ,所以这个要自己去判断。所有 build-in 的 functions 都在适当的时候会进行 type checking ,并且会在不匹配的时候产生 wrong-type-argument 的错误信号。例如:
(+ 2 'a)
Emacs Lisp 提供了这些判断 argument 是否是特定类型的 object ,它们称为 type predicate function 。它们接受一个 argument ,如果是特定的 type 就返回 t ,否则返回 nil 。这些 function 的名字,大都是以 "p" 结束的。
Equality Predicates
eq object1 object2 function :如果 object1 与 object2 是同一个 object 则返回 t ,否则返回 nil 。这意味着,更改其中的一个 object 的 content ,另一个也会跟着更改的。即,它们实际上是指向同一个 object 的时候,才会返回 t 。
(eq 'foo 'foo) ⇒ t (eq 456 456) ⇒ t (eq "asdf" "asdf") ⇒ nil (eq "" "") ⇒ t ;; This exception occurs because Emacs Lisp ;; makes just one multibyte empty string, to save space. (eq '(1 (2 (3))) '(1 (2 (3)))) ⇒ nil (setq foo '(1 (2 (3)))) ⇒ (1 (2 (3))) (eq foo foo) ⇒ t (eq foo '(1 (2 (3)))) ⇒ nil (eq [(1 2) 3] [(1 2) 3]) ⇒ nil (eq (point-marker) (point-marker)) ⇒ nil
equal object1 object2 function : 如果 object1 和 object2 有相同的 components ,则返回 t ,否则返回 nil 。它是比较内容是否一样的。如果一个 object 是 eq 另一个 object ,则一定也是 equal 的,但反过来则不一定。
(equal 'foo 'foo) ⇒ t (equal 456 456) ⇒ t (equal "asdf" "asdf") ⇒ t (eq "asdf" "asdf") ⇒ nil (equal '(1 (2 (3))) '(1 (2 (3)))) ⇒ t (eq '(1 (2 (3))) '(1 (2 (3)))) ⇒ nil (equal [(1 2) 3] [(1 2) 3]) ⇒ t (eq [(1 2) 3] [(1 2) 3]) ⇒ nil (equal (point-marker) (point-marker)) ⇒ t (eq (point-marker) (point-marker)) ⇒ nil
Numbers
Integer Basics
非十进制的 integer 可以通过下面的形式来指定:
- #b1010101
- 表示二进制
- #o52
- 表示八进制 octal
- #x2c
- 表示十六进制 hex
- #radixrInteger
- 表示以 radixr 为进制的数据 。例如 :#24r1k
integer 的最大值,可以通过 variable most-positive-fixnum 来获取 。最小值可以通过 most-negative-fixnum 来获取 :
下面是我在 Mac 下的输出。
most-positive-fixnum 2305843009213693951 most-negative-fixnum -2305843009213693952
Floating-Point Basics
它的范围,与 C 语言在你的机器中 double 所能代表的范围。
Comparison of Numbers
你应该使用 = 来比两个数是否相等,而不是用 eq 。 = 仅比较一个 object 的 numeric values ,而不是比较它是否是同一个 object 。
Numeric Conversions
integer => floating point ,使用 float function 。 floating point => integer 则有4个 function:
- truncate , 向 0 的方向取整。
- floor ,向负无穷的方向取整。
- ceiling ,向正无穷的方向取整。
- round ,朝向最近的 integer 取整。(俗称四舍五入)
Strings and Characters
basics
charater 只是 integer 。一个 integer 是否是一个 charater 取决于它如何使用的。 不像 C ,Emacs Lisp 的 strings 没有用终止字符码。
strings 是 arrays ,因此也是 sequences ,所以,你可以用 array 和 sequences 相关的 function 来操作它。
不过要注意, length 不应该用来计算 string 的长度,请用 string-width 来代替。
它有两种表示:
- unibyte
- multibyte
Evaluation
在 Emacs Lisp 中将一个 expression 进行 evaluation 是由解析器执行的——将接收到的 Lisp object 的输入作为一个程序,然后进行计算它的值。解析器会自动地进行 evaluate ,但也可以显式地调用 eval 来执行。
evaluation 简介
一个试图准备 evaluation 的 Lisp object 被称为一个 form 或 expression 。 form 是数据对象而不仅仅是文本这个事实,是类Lisp语言与其他语言的根本区别之一。任何对象都可以被 evaluated ,但通常仅是 numbers, symbols, lists 和 strings 。
读取然后 evaluate form 是非常觉的,但是,读取和 evaluation 是分开的两个动作的的,并且可以单独地执行。
读取但没有进行任何的 evaluate 时,它将 Lisp object 的 printed representation 转换为 object 本身。
evaluation 是一个递归处理,evaluate 一个 form 通常需要 evaluate 该 form 里的每部分。
Forms 的类型
Self-Evaluating Forms
即除了 list 和 symbol 外的 form 。 Self-evaluating evaluate 为它自身,即它们进行 evaluate 之后,还是它们自身。因此, number 25 evaluate 为 25 ;string "foo" evaluate 之后还是 "foo" 。同样, evaluate 一个 vector 并不会导致将 vector 的 element 进行 evaluate ,它返回的是 vector 自身并且内容不变。
- numbers
- characters
- strings
- vectors
都属于此类。
Symbol Forms
当一个 symbol 进行 evaluate 时,它被视为一个 variable 。它的结果就是 variable 的 value ,如果有的话。如果 symbol 并没有作为 variable 的 value 的话,Lisp 解析器会产生一个错误。例如:
(setq a 123) ⇒ 123 (eval 'a) ⇒ 123 a ⇒ 123
symbol 中的 nil 和 t 是特殊对待的, nil 的 value 永远是 nil , t 的 value 永远是 t ,并且你不能设置或绑定其他 value 给它们。
一些以 : 开头的 symbol 也是 self-evaluate 的, 同样地,它的 value 通常也是不能更改的。
Classification of List Forms
一个 form 如果它是非空的 list ,则会根据它的第一个 element 来判断它是否是一个 function call , 或 macro call 或 special form 。这三种类型的 form 是用不同的方式来进行 evaluate 。剩下的 list 元素组成这三种类型的 arguments 。
在一个非空的 list 中,evaluating 的第一步就是检测它的第一个 element 。这个 element 决定了该 form 是什么类型,以及list中剩下的元素将如何处理。 不过,在一些 Lisp dialects 中,第一个元素并不会被 evaluated ,比如 Scheme 。
Symbol Function Indirection
如果 list 中的第一个 element 是一个 symbol ,然后 evaluation 测试该 symbol 的 function cell ,并使用它的内容来代替原始的 symbol 。如果它的内容是其他的 symbol ,这种处理,就称为 symbol function indirection ,它会一直这样子重复处理,直到包含一个非 symbol 。
Evaluation of Function Forms
如果 list 中的第一个 element 是一个 Lisp function object 或 byte-code object 或 primitive function object ,这种 list 就称为一个 function call 。例如
(+ 1 x)
evaluate 一个 function call 的第一步就是从左到右地进行 evaluate 这个 list 中的剩余的元素。结果就是 argument 的实际 value ,每一个 element 对应一个 value 。下一步就是将这些 argument 的value 进行 function call 。
Lisp Macro Evaluation
如果准备进行 evaluated 的 list 的第一个 element 是一个 macro object ,则这个 list 就是一个 macro call 。 当它进行 evaluating 时,剩下的 list elements 并不会初始化地 evaluate 。相反,这些 elements 自身是作为这个 macro 的 arguments 。 macro 的定义计算出一个代替的 form ,称为 macro 的 expression, 被 evaluated 后就会替代原来的 form 。这个 expansion 可能是以下的 form :一个 self-evaluating 常量,一个 symbol, 或一个 list 。如果该 expansion 自身还是一个 macro call ,则进行递归处理,直到结果是其他形式的 form 。
Special Forms
一个 special form 是一个特别标识的 primitive function ,它的 arguments 并不会进行 evaluated 。绝大部分的 special form 定义控制结构或执行 variable bindings —— 这些是 function 做不到的。
如果一个 expression 的第一个 symbol 是一个 special form ,则该 expression 应该遵循这个 special form 的规则,否则, Emacs 的行为是未定义的(尽管它不会崩溃)。例如 ((lambda (x) x . 3) 4)
是一个以 lambda 开头的但并不是 well-formed 的 lambda expression ,所以 Emacs 可能会产生一个错误信息,或者返回 或 4 或 nil ,又或者是其他方式的行为。
Autoloading
autoload 特性允许你调用一个还没有加载到 Emacs 的 function 或 macro 。它指出哪个文件包含它的定义。当一个 autoload object 作为一个 symbol 的 function definition ,作为 function 来调用这个 symbol 将会自动地加载所在的文件;然后从该文件中加载它真正要调用的 definition 。
Quoting
quote 这个 special form 返回它的单个 arguments,写的是什么就返回什么,并且不会 evaluating 。在程序中,这提供了一个包含 constant symbol 和 lists 的方式,它们并不是 self-evaluating object 。
并不必要去 quote 一个 self-evaluating objects ,比如 numbers, strings, 和 vectors 。
例子:
(quote (+ 1 2)) ⇒ (+ 1 2) (quote foo) ⇒ foo 'foo ⇒ foo ''foo ⇒ (quote foo) '(quote foo) ⇒ (quote foo) ['foo] ⇒ [(quote foo)]
Backquote
backquote 结构允许你去 quote 一个 list ,但是选择性地进行 evaluate 该 list 的部分 element 。它与 quote 相同,例如:
`(a list of (+ 2 3) elements) ⇒ (a list of (+ 2 3) elements) '(a list of (+ 2 3) elements) ⇒ (a list of (+ 2 3) elements)
在 backquote 中的 arguments 中,可以有一个特殊标记 ,
来指示一个 value 它并不是 constant 的。Emacs Lisp evaluator 会 evaluate 那些带有 ,
的 arguments,然后将 evaluate 后的 value 放回到 list 结构中。例如:
`(a list of ,(+ 2 3) elements) ⇒ (a list of 5 elements)
并且 ,
允许在list结构中更深层次中使用,例如:
`(1 2 (3 ,(+ 4 5))) ⇒ (1 2 (3 9))
你也可以拼接一个被 evaluated 的 value 到结果的 list 中, 使用特殊标记 ,@
。这种拼接的结果的 elements 与其他 elements 会处于同等的级别。例如:
(setq some-list '(2 3)) ⇒ (2 3) (cons 1 (append some-list '(4) some-list)) ⇒ (1 2 3 4 2 3) `(1 ,@some-list 4 ,@some-list) ⇒ (1 2 3 4 2 3) (setq list '(hack foo bar)) ⇒ (hack foo bar) (cons 'use (cons 'the (cons 'words (append (cdr list) '(as elements))))) ⇒ (use the words foo bar as elements) `(use the words ,@(cdr list) as elements) ⇒ (use the words foo bar as elements)
Eval
大部分情况下,在正被执行的程序中出现的 form 会自动地被 evaluate 。在一个罕见的时候,你可能需要在 runtime 的时候 evaluate 写的代码,比如从被编辑的文本中读取一个 form 或从一个 list property 中获取。在这种情况下,可以使用 eval function 。
通常情况下是不需要 eval 的,并且有其他的东西可以代替它。例如,为了获取一个 variable 的 value ,虽然 eval 也可以,但更好的是使用 symbol-value ;或者其他保存在一个 list property 中的 expression 然后需要进行 eval 的,当它是 function 的时候,更好的方式是使用 funcall 而不是 eval 。
Control Structures
Sequence
例如:
(progn (print "The first form") (print "The second form") (print "The third form"))
progn 的返回值是最后一个 expression 的返回值。它们按顺序从上到下执行。
Conditionals
if
(if nil (print 'true) 'very-false)
when
(when condition a b c)
它等同于:
(if condition (progn a b c) nil)
unless
(unless condition a b c)
它等同于:
(if condition nil a b c)
cond
(cond (case1 do-when-case1) (case2 do-when-case2) ... (t do-when-none-meet))
Constructs for Combining Conditions
and
(and (print 1) (print 2) nil (print 3)) -| 1 -| 2 ⇒ nil
or
(or (eq x nil) (eq x 0))
not
(not nil) => t (not t) => nil
Iteration
while
(setq num 0) ⇒ 0 (while (< num 4) (princ (format "Iteration %d." num)) (setq num (1+ num))) -| Iteration 0. -| Iteration 1. -| Iteration 2. -| Iteration 3. ⇒ nil
Macro: dolist (var list [result]) body
(defun reverse (list) (let (value) (dolist (elt list value) (setq value (cons elt value)))))
dotimes
(dotimes (i 100) (insert "I will not obey absurd orders\n"))