递归类型是一种具有基础和自身递归情况的类型。
我希望这能实现“类型列表”,即其conses只允许相同元素类型或nil的列表。
我尝试了以下定义:
(deftype list-of (a) `(or null
(cons ,a (list-of ,a))))
然而,由于编译器试图无限期地在列表上进行递归,因此这表示堆栈耗尽问题(至少在SBCL上)。是否可以定义这样的数据类型?
答案 0 :(得分:5)
这是不可能的。您使用DEFTYPE
定义的类型是"派生类型"。派生类型(如宏)扩展为"真实"类型说明符,它不能包含派生类型。扩展中对派生类型(类型本身或其他类型)的所有引用也都会扩展。因此,递归类型将进入infite循环以尝试扩展。
Trivial Types为正确列表提供了一种类型,但即使将其作为参数,也不会实际检查元素类型。出于美观的原因,这就足够了。
(ql:quickload :trivial-types)
(use-package :trivial-types)
(typep '("qwe" "asd" "zxc") '(proper-list string)) ;=> T
(typep '("qwe" "asd" "zxc" 12) '(proper-list string)) ;=> T
否则,您可以定义一种类型,检查前几个元素是否是正确的类型。这至少会抓住最明显的违规行为。
(deftype list-of (a)
`(or null (cons ,a (or null (cons ,a (or null (cons ,a list)))))))
(typep '("asd") '(list-of string)) ;=> T
(typep '("asd" 12) '(list-of string)) ;=> NIL
(typep '("asd" "qwe") '(list-of string)) ;=> T
(typep '("asd" "qwe" 12) '(list-of string)) ;=> NIL
(typep '("asd" "qwe" "zxc") '(list-of string)) ;=> T
(typep '("asd" "qwe" "zxc" 12) '(list-of string)) ;=> T
如果您希望它适用于任何长度的列表,您必须为您需要的每个不同列表定义类型。
(defun list-of-strings-p (list)
(every #'stringp list))
(deftype list-of-strings ()
`(or null (satisfies list-of-strings-p)))
(typep '("qwe" "asd" "zxc" "rty" "fgh") 'list-of-strings) ;=> T
(typep '("qwe" "asd" "zxc" "rty" "fgh" 12) 'list-of-strings) ;=> NIL
答案 1 :(得分:3)
是的,但我作弊了;)
(defun satisfication (a)
(if a
(and (integerp (car a))
(satisfication (cdr a)))
T))
(deftype my-list () `(satisfies satisfication))
(typep (cons 1 (cons 2 (cons 3 nil))) 'my-list)
> T
(typep (cons 1 (cons 2 (cons 3.2 nil))) 'my-list)
> NIL
显然SBCL不喜欢递归类型 - 另一个答案很好地解释了原因。但是如果你想坚持使用标准并仍然定义递归类型,隧道尽头就会有一个亮点:你可以定义任何函数来检查满意度。