14.1 类型标识符 (Type Specifiers)
类型在 Common Lisp 里不是对象。举例来说,没有对象对应到 integer
这个类型。我们像是从 type-of
函数里所获得的,以及作为传给像是 typep
函数的参数,不是一个类型,而是一个类型标识符 (type specifier)。
一个类型标识符是一个类型的名称。最简单的类型标识符是像是 integer
的符号。这些符号形成了 Common Lisp 里的类型层级。在层级的最顶端是类型 t
── 所有的对象皆为类型 t
。而类型层级不是一棵树。从 nil
至顶端有两条路,举例来说:一条从 atom
,另一条从 list
与 sequence
。
一个类型实际上只是一个对象集合。这意味着有多少类型就有多少个对象的集合:一个无穷大的数目。我们可以用原子的类型标识符 (atomic type specifiers)来表示某些集合:比如 integer
表示所有整数集合。但我们也可以建构一个复合类型标识符 (compound type specifiers)来参照到任何对象的集合。
举例来说,如果 a
与 b
是两个类型标识符,则 (or a b)
表示分别由 a
与 b
类型所表示的联集 (union)。也就是说,一个类型 (or a b)
的对象是类型 a
或 类型 b
。
如果 circular?
是一个对于 cdr
为环状的列表返回真的函数,则你可以使用适当的序列集合来表示: [1]
(or vector (and list (not (satisfies circular?))))
某些原子的类型标识符也可以出现在复合类型标识符。要表示介于 1 至 100 的整数(包含),我们可以用:
(integer 1 100)
这样的类型标识符用来表示一个有限的类型 (finite type)。
在一个复合类型标识符里,你可以通过在一个参数的位置使用 *
来留下某些未指定的信息。所以
(simple-array fixnum (* *))
描述了指定给 fixnum
使用的二维简单数组 (simple array)集合,而
(simple-array fixnum *)
描述了指定给 finxnum
使用的简单数组集合 (前者的超类型 「supertype」)。尾随的星号可以省略,所以上个例子可以写为:
(simple-array fixnum)
若一个复合类型标识符没有传入参数,你可以使用一个原子。所以 simple-array
描述了所有简单数组的集合。
如果有某些复合类型标识符你想重复使用,你可以使用 deftype
定义一个缩写。这个宏与 defmacro
相似,但会展开成一个类型标识符,而不是一个表达式。通过表达
(deftype proseq ()
'(or vector (and list (not (satisfies circular?)))))
我们定义了 proseq
作为一个新的原子类型标识符:
> (typep #(1 2) 'proseq)
T
如果你定义一个接受参数的类型标识符,参数会被视为 Lisp 形式(即没有被求值),与 defmacro
一样。所以
(deftype multiple-of (n)
`(and integer (satisfies (lambda (x)
(zerop (mod x ,n))))))
(译注: 注意上面代码是使用反引号 `
)
定义了 (multiple-of n) 当成所有 n
的倍数的标识符:
> (type 12 '(multiple-of 4))
T
类型标识符会被直译 (interpreted),因此很慢,所以通常你最好定义一个函数来处理这类的测试。