当所有简单类型都不适用时,你可以使用复合类型——这类类型基于其他类型或指定数据 构建新类型。被指定的类型或数据称为复合类型的参数(arguments)。复合类型的标准格式如下:
(constructor arguments...)
你也可以在参数前添加“关键字-值”对,格式如下:
(constructor {keyword value}... arguments...)
下表列出了各类构造器,以及如何使用它们定义复合类型:
(cons car-type cdr-type)值必须是一个 cons 单元(cons cell),其 CAR 部分需符合 car-type 类型,
CDR 部分需符合 cdr-type 类型。例如,(cons string symbol)
是一种自定义类型,可匹配 ("foo" . foo) 这类值。
在自定义缓冲区中,CAR 和 CDR 会分别显示和编辑, 各自按照其指定的类型进行处理。
(list element-types…)值必须是一个列表,其元素个数与给出的 element-types 数量完全一致; 并且每个元素必须符合对应的 element-type 类型。
例如,(list integer string function) 描述了一个包含三个元素的列表;
第一个元素必须是整数,第二个是字符串,第三个是函数。
在自定义缓冲区中,每个元素会根据为其指定的类型分别显示和编辑。 The value must be a list with exactly as many elements as the
(group element-types…)用法与 list 类似,区别仅在于自定义缓冲区中文本的显示格式。
list 会为每个元素值加上标签;而 group 不会。
(vector element-types…)与 list 类似,区别是值必须是向量(vector)而不是列表。
元素的用法与 list 中相同。
(alist :key-type key-type :value-type value-type)值必须是一个由 cons 单元组成的列表,每个单元的 CAR 表示一个键, 其自定义类型为 key-type;同一个单元的 CDR 表示一个值, 其自定义类型为 value-type。 用户可以添加和删除键/值对,并编辑每一对的键和值。
如果省略,key-type 和 value-type 默认均为 sexp。
用户可以添加任何符合指定键类型的键,但你可以通过 :options
关键字指定某些键,使其得到优先显示(see 定义自定义变量)。
这些指定的键会始终显示在自定义缓冲区中(并附带合适的值),
并带有一个复选框,用于在关联列表中启用、禁用或排除该键/值对。
用户无法编辑由 :options 关键字参数指定的键。
:options 关键字的参数应该是一个列表,
列出关联列表中合理的键的说明。通常它们只是原子(atom),表示自身。例如:
:options '("foo" "bar" "baz")
该写法指定了三个已知的键,即 "foo"、"bar" 和 "baz",
这些键会始终优先显示。
你可能希望为特定键限制值类型,例如,与 "bar" 键关联的值只能是整数。
要实现这一点,可将列表中的原子替换为子列表:第一个元素仍像之前一样指定键,
第二个元素则指定该键对应的值类型。例如:
:options '("foo" ("bar" integer) "baz")
最后,你可能希望修改键的展示方式。默认情况下,
由于用户无法修改通过 :options 关键字指定的特殊键,
这些键会以 const 类型直接显示。但你可以为键指定更专用的展示类型——
比如若已知该键是绑定了函数的符号,可使用 function-item 类型。
实现方式是将键的符号替换为自定义类型规范:
:options '("foo"
((function-item some-function) integer)
"baz")
许多关联列表(alist)会使用双元素列表而非 cons 单元。例如:
(defcustom list-alist
'(("foo" 1) ("bar" 2) ("baz" 3))
"Each element is a list of the form (KEY VALUE).")
而非:
(defcustom cons-alist
'(("foo" . 1) ("bar" . 2) ("baz" . 3))
"Each element is a cons-cell (KEY . VALUE).")
由于列表是基于 cons 单元实现的,你可以将上述示例中的 list-alist
视作 cons 单元形式的关联列表(alist)——其中值类型是仅包含一个元素的列表,
该元素存储实际值。
(defcustom list-alist '(("foo" 1) ("bar" 2) ("baz" 3))
"Each element is a list of the form (KEY VALUE)."
:type '(alist :value-type (group integer)))
此处使用 group 界面组件而非 list,仅因为其显示格式更贴合该场景的需求。
同理,你可以通过这一技巧的变体,实现一个键关联多个值的关联列表:
(defcustom person-data '(("brian" 50 t)
("dorith" 55 nil)
("ken" 52 t))
"Alist of basic info about people.
Each element has the form (NAME AGE MALE-FLAG)."
:type '(alist :value-type (group integer boolean)))
(plist :key-type key-type :value-type value-type)该自定义类型与 alist(见上文)类似,但存在两点区别:
(i) 信息以属性列表(property list)的形式存储(see 属性列表);
(ii) 若省略 key-type,其默认值为 symbol 而非 sexp。
(choice alternative-types…)值必须符合 alternative-types 中的某一种类型。例如,
(choice integer string) 允许值为整数或字符串。
在自定义缓冲区中,用户可通过菜单选择其中一种类型, 随后按照该类型的常规方式编辑对应的值。
通常,此菜单中的字符串由选项自动决定;但你可以在备选类型中使用 :tag 关键字,
为菜单指定不同的显示文字。例如,若一个整数表示空格数量,而字符串表示直接使用的文本,
你可以这样编写自定义类型:
(choice (integer :tag "Number of spaces")
(string :tag "Literal text"))
这样菜单就会显示 ‘Number of spaces(空格数量)’ 和 ‘Literal text(原文文本)’。
对于 nil 不是有效值、且不是 const 类型的备选选项,
你应当使用 :value 关键字为其指定一个有效的默认值。See 类型关键字。
如果某个值可以匹配多个备选类型,自定义系统会选择第一个能匹配该值的类型。 这意味着你应当将最具体的类型放在前面,最通用的类型放在最后。 下面是正确用法示例:
(choice (const :tag "Off" nil)
symbol (sexp :tag "Other"))
这样一来,特殊值 nil 就不会被当作普通符号处理,
符号也不会被当作普通 Lisp 表达式处理。
(radio element-types…) ¶与 choice 类似,区别在于选项以**单选按钮**而非菜单显示。
这样做的好处是,在适用时可以显示选项的说明文档,
因此常用于在多个常量函数(function-item 自定义类型)之间选择。
(const value)值必须是 value — 不允许其他任何值。
const 的主要用途是用在 choice 内部。例如,
(choice integer (const nil)) 允许值为整数或 nil。
:tag 经常与 const 配合,用在 choice 内部。
例如:
(choice (const :tag "Yes" t)
(const :tag "No" nil)
(const :tag "Ask" foo))
这段代码描述了一个变量:t 表示“是”,nil 表示“否”,
foo 表示 “询问(ask)”。
(other value)该备选选项可以匹配任意 Lisp 值,但如果用户选择此选项, 就会选用值 value。
other 的主要用途是作为 choice 的最后一个元素。
例如:
(choice (const :tag "Yes" t)
(const :tag "No" nil)
(other :tag "Ask" foo))
这段代码描述了一个变量:t 表示“是”,nil 表示“否”,
其他任何值都表示 “询问(ask)”。
如果用户从选项菜单中选择 ‘询问Ask’,就会设定值为 foo;
而任何其他值(非 t、非 nil、非 foo)
都会像 foo 一样显示为 ‘询问Ask’。
(function-item function)与 const 类似,但专用于表示函数类型的值。
它会同时显示函数名和文档字符串。
文档字符串可以是通过 :doc 指定的内容,
也可以是 function 自身自带的文档字符串。
(variable-item variable)与 const 类似,但专用于表示变量名类型的值。
它会同时显示变量名和文档字符串。
文档字符串可以是通过 :doc 指定的内容,
也可以是 variable 自身自带的文档字符串。
(set types…)值必须是一个列表,且列表中的每个元素必须匹配指定的 types 中的某一个。
它在自定义缓冲区中以复选清单的形式显示,
因此每个 types 最多只能对应一个元素,也可以没有。
不允许出现匹配同一类型的多个不同元素。
例如,(set integer symbol) 只允许列表中包含一个整数和/或一个符号;
不允许多个整数或多个符号。因此,在 set 中很少使用
integer 这类不具体的类型。
set 中的 types 绝大多数情况下都是 const 类型,如下例:
(set (const :bold) (const :italic))
有时它们用于描述关联列表(alist)中的可选元素:
(set (cons :tag "Height" (const height) integer)
(cons :tag "Width" (const width) integer))
这样就可以让用户 可选地 指定高度值和宽度值。
(repeat element-type)值必须是一个列表,且列表中的每个元素都必须符合 element-type 类型。 它在自定义缓冲区中显示为元素列表,带有 ‘[INS]’ 和 ‘[DEL]’ 按钮, 用于添加或删除元素。
(restricted-sexp :match-alternatives criteria) ¶这是最通用的复合类型构造器。 值可以是任何满足 criteria 中任一条件的 Lisp 对象。 criteria 必须是一个列表,其中每个元素可以是以下形式之一:
nil 或非 nil。
在列表中使用谓词意味着,
谓词返回非 nil 的对象都是可接受的值。
'object。
列表中的此类元素表示,object 本身就是可接受的值。
例如:
(restricted-sexp :match-alternatives
(integerp 't 'nil))
该写法允许整数、t 和 nil 作为合法值。
在自定义缓冲区中,所有合法值都会使用其读取语法(read syntax)显示, 用户可以通过文本方式编辑它们。
下面是复合类型中可用于关键字-值对的关键字列表: