Emacs Lisp 数据类型
目录

1. 原子(atom)数据类型


在Lisp中,除了序对(cons cells,下节介绍)组成的类型之外的其它所有类型称为原子(atom)类型。 以下是常见的原子类型:

1.1 数字 integer & floating

数字包括整数(Integer, 30bits),浮点数(Floating, double)。 可以从most-positive-fixnummost-negative-fixnum两个变量得到整数的‰围。 NaN也表示特殊值。

相关函数:

;; 测试函数
(integerp OBJECT)
(floatp OBJECT)
(numberp OBJECT)
(zerop NUMBER)
(wholenump OBJECT)
;; 比较函数
(> NUM1 NUM2)
(< NUM1 NUM2)
(>= NUM1 NUM2)
(<= NUM1 NUM2)
(= NUM1 NUM2)
(eql OBJ1 OBJ2)
(/= NUM1 NUM2)
;; 转换函数
(float ARG)
(truncate ARG &optional DIVISOR)
(floor ARG &optional DIVISOR)
(ceiling ARG &optional DIVISOR)
(round ARG &optional DIVISOR)
;; 运算
(+ &rest NUMBERS-OR-MARKERS)
(- &optional NUMBER-OR-MARKER &rest MORE-NUMBERS-OR-MARKERS)
(* &rest NUMBERS-OR-MARKERS)
(/ DIVIDEND DIVISOR &rest DIVISORS)
(1+ NUMBER)
(1- NUMBER)
(abs ARG)
(% X Y)
(mod X Y)
(sin ARG)
(cos ARG)
(tan ARG)
(asin ARG)
(acos ARG)
(atan Y &optional X)
(sqrt ARG)
(exp ARG)
(expt ARG1 ARG2)
(log ARG &optional BASE)
(log10 ARG)
(logb ARG)
;; ‘随机数
(random &optional N)

1.2 字符 character

字符(Character, 包括ASCII和宽字符,22bits)。字符也是数字。

字符读入语法是在字符前加上问号。比如?A代表字符A。 和C相同,一些控制字符需要加上转义符,如制表符?\t

\M-表示Meta/Alt键,\C-表示Ctrl键。

相关函数:

;; 测试函数
(stringp OBJECT)
(string-or-null-p OBJECT)
(char-or-string-p OBJECT)
;; 构造函数
(make-string LENGTH INIT)
(string &rest CHARACTERS)
(substring STRING FROM &optional TO)
(concat &rest SEQUENCES)
;; 比较函数
(char-equal C1 C2)
(string= S1 S2)
(string-equal S1 S2)
(string< S1 S2)
;; 转换函数
(char-to-string CHAR)
(string-to-char STRING)
(number-to-string NUMBER)
(string-to-number STRING &optional BASE)
(downcase OBJ)
(upcase OBJ)
(capitalize OBJ)
(upcase-initials OBJ)
(format STRING &rest OBJECTS)
;; 查找与替换
(string-match REGEXP STRING &optional START)
(replace-match NEWTEXT &optional FIXEDCASE LITERAL STRING SUBEXP)
(replace-regexp-in-string REGEXP REP STRING &optional FIXEDCASE LITERAL SUBEXP START)
(subst-char-in-string FROMCHAR TOCHAR STRING &optional INPLACE)

1.3 符号 symbol

在Lisp中使用变量的必须通过符号(Symbol)类型来实现。 符号类型由4个部分组成:。:

  • 符号名(Name):只能绑定string对象。symbol-name访问。
  • 符号值(Value):可以绑定任意类型。symbol-value访问,defvardefconst赋值。
  • 函数(Function):应该绑定function对象或macro对象。symbol-function访问,defundefmacro赋值。
  • 属性列表(Property list):应该绑定prperty list对象。symbol-plist访问。

其中符号名必须绑定一个string对象,其它三个部分可以独立地绑定任何类型的对象。 其中符号值和函数部分可以为空。 因此符号类型是一种符号名到其它三个绑定对象的映射关系。

符号名是由字符串组成,有命名规则。 符号名具有唯一性。 全局变量obarray记录着emacs中所有全局符号名到符号映射。 也可以说这些符号使用相同的命名空间。 setq可以设置全局变量,并绑定符号值或函数部分。 也可以创建新的obarray,由set加入符号。 obarray是自动增长的vector类型,通过hash符号名找到符号在vector中的位置。 vector中每个元素是一个bucket,在bucket中符号由链表连接。 在ELisp中只能通过make-vector创建obarray,intern插入符号。

符号在Lisp求值时会根据符号名取出符号对象,然后返回符号值或函数并求值。 而其它原子类型(数字,字符)都是自求值:简单返回自身。

下面两种特殊的符号的符号值不能修改(也可以看成自求值的):

  1. 关键字(keywork)符号:符号名以:开始的符号。
  2. tnil表示的真假特殊符号。

在Lisp中,符号nil有3三种不同的意义:

  • 逻辑上代表事实中的“假”
  • 一个名为nil的符号类型
  • 一个空的表类型,等价()
(symbolp OBJECT)
(intern-soft NAME &optional OBARRAY)
(intern STRING &optional OBARRAY)
(unintern NAME &optional OBARRAY)
(mapatoms FUNCTION &optional OBARRAY)
(symbol-name SYMBOL)
(symbol-value SYMBOL)
(boundp SYMBOL)
(set SYMBOL NEWVAL)
(setq SYM VAL SYM VAL ...)
(symbol-function SYMBOL)
(fset SYMBOL DEFINITION)
(fboundp SYMBOL)
(symbol-plist SYMBOL)
(get SYMBOL PROPNAME)
(put SYMBOL PROPNAME VALUE)

2. 复合数据类型


2.1 序对 Cons Cells

Lisp提供一种称为序对(Cons Cells, construction of cells,或称Conses)的复合结构。

序对包含两个对象(或称槽slot),第一个对象叫CAR(Contents of Address part of Register), 第二个对象叫CDR(Contents of the Decrement part of Register)。 每个对象可以是任何类型,这是cons的闭包性质(cons 1 2)表示用盒子指针表示:

对于C程序员来说:Lisp中并不区分保存一个值和指向一个值,因为指针在Lisp中是隐式的。

如前面所示,序对可以用函数cons构造出来(cons CAR CDR)

(defvar x (cons 1 (cons 2 3)))    ; => x
(car x)                           ; => 1
(cdr x)                           ; => (2 . 3)
(car (cdr x))                     ; => 2
(cdr (cdr x))                     ; => 3

(CAR . CDR)也是一种构造序对的方法:

(cons 1 (cons 2 3))               ; => (1 2 . 3)
'(1 . (2 . 3))                    ; => (1 2 . 3)

序对可以用作构造任意种类的复杂数据结构的通用的基本构件,也是Lisp的核心数据结构类型。 (这和哲学中任何多元论都可转变为二元论的思想一致。) 正因为序对如此重要,我们把Lisp中不是序对的对象称为原子(atom)

2.2 序列 Sequence

序列表示的是有序元素的集合。 在Emacs Lisp中,序列有两种类型:列表(list)数组(array)

一些叫做序列函数的函数,可以接受任何类型的序列。

2.3 列表 list

采用序对表示序列的方式很多,一种最直接的表示方式是把序对串成单链表一样, 每个序对的car部分对应每个链中的条目,cdr部分则是链中下一个序对,最后一个序对的cdr用nil表示。 这种嵌套序对形成的序列称为表(list)

()nil也可以当作一个不包含任何元素的序列,称为空表。 因此(A ())等价于(A nil)

除了用cons.嵌套地定义表之外,lisp方便地提供基本操作list来构造表。

(cons a1 (cons a2 (cons ... (cons an nil) ... )))
(a1 . (a2 ... (an-1 . (an)) ... ))
(list a1 a2 ... an)

显然,cons可以用于在已有的表的最前面添加一个元素。 append用于合并两个表。 length用于返回表的长度。

(defvar l (list 1 2 3))        ; => l
(cons 0 l)                     ; => (0 1 2 3)
(append l l)                   ; => (1 2 3 1 2 3)
(length l)                     ; => 3

mapc,mapcar,dolist用于遍历list。

取出list元素的操作有nth,获得列表一个区间的函数有nthcdrlastbutlast

2.3.1 列表模拟其它数据结构

使用push,pop操作操纵list可以模拟的行为。

使用append,delete-dups,memq,member,delq,remq操作操纵list可以模拟集合的行为。

把序对的看成指向左右孩子节点的指针,可以把这种结构看成二叉树。进一步推广,可以看成

也可以实现association list关联列表(和2.6节hash table有区别)。

list相关函数:

;; 测试
(consp OBJECT)
(listp OBJECT)
(null OBJECT)
;; 构造
(cons CAR CDR)
(list &rest OBJECTS)
(append &rest SEQUENCES)
;; –访问list元素
(car LIST)
(cdr LIST)
(cadr X)
(caar X)
(cddr X)
(cdar X)
(nth N LIST)
(nthcdr N LIST)
(last LIST &optional N)
(butlast LIST &optional N)
;; 修改 cons cell
(setcar CELL NEWCAR)
30 1ÊÙ Äêâa.ƒn cons cell ÚL
(setcdr CELL NEWCDR)
;; list操作
(push NEWELT LISTNAME)
(pop LISTNAME)
(reverse LIST)
(nreverse LIST)
(sort LIST PREDICATE)
(copy-sequence ARG)
(nconc &rest LISTS)
(nbutlast LIST &optional N)
;; 集合函数
(delete-dups LIST)
(memq ELT LIST)
(member ELT LIST)
(delq ELT LIST)
(delete ELT SEQ)
(remq ELT LIST)
(remove ELT SEQ)
;; 关联列表
(assoc KEY LIST)
(assq KEY LIST)
(assoc-default KEY ALIST &optional TEST DEFAULT)
(rassoc KEY LIST)
(rassq KEY LIST)
;; 遍历函数
(mapc FUNCTION SEQUENCE)
(mapcar FUNCTION SEQUENCE)
(dolist (VAR LIST [RESULT]) BODY...)
;; 其它
(number-sequence FROM &optional TO INC)
(split-string STRING &optional SEPARATORS OMIT-NULLS)
(mapconcat FUNCTION SEQUENCE SEPARATOR)
(identity ARG)

2.4 数组 array

数组存放在内存中的连续区域,并且支持随机访问,也就是说访问元素的时间和位置无关,而表的访问时间和元素位置成正比。 数组下标从0开始。 向量中元素可以为任何类型。 数组是自求值的。

Emacs Lisp中只有一维数组,不存在多维数组,但可以通过嵌套一维数组组成多维数组。

数组进一步可以划分为字符串(string)向量(vector)字符表(char-table)布尔向量(bool-vector)。 每种数组类型都有他们各自的读写函数。

2.4.1 字符串 string

字符串是字符的数组。字符串是常量,只能创建,不能修改(?)。

字符串中元素只能是字符,但字符串包含字符的文本属性,这和字符组成的向量不同。 如果元素是字符时, 使用字符串相比向量更好, 因为字符串需要的空间更少 (只需要向量的 1/4), 输出更直观,能用文本属性(text property),能使用 emacs 的 IO 操作Š。

2.4.2 向量 vector

向量是一种通用的一维数组,它的元素可以是任意的对象。

2.4.3 字符表 char-table

字符表是可被字符索引的数组,除此之外,其它很像向量。

2.4.4 布尔向量 bool-vector

布尔向量中的元素只能是tnil

数组相关函数

;; 测试
(sequencep OBJECT)
(arrayp OBJECT)
(vectorp OBJECT)
(char-table-p OBJECT)
(bool-vector-p OBJECT)
;; 序列函数
(length SEQUENCE)
(safe-length LIST)
(elt SEQUENCE N)
(copy-sequence ARG)
(copy-tree TREE &optional VECP)
;; 数组
(vector &rest OBJECTS)
(make-vector LENGTH INIT)
(aref ARRAY IDX)
(aset ARRAY IDX NEWELT)
(vconcat &rest SEQUENCES)
(append &rest SEQUENCES)

2.5 函数 function

在Lisp中,函数是Lisp对象,是第一类型。 一个未编译的函数是一个表,其第一个元素是符号lambda

Lisp中允许使用lambda定义匿名函数(anonymous function)。 一个命名函数(named function)只是一个符号,其函数单元(function cell)指向一个有效函数。

2.6 哈希表 hash table

哈希表是一个快速索引表。

参考

发表评论