是否可以在Common Lisp中定义递归类型?

时间:2016-05-18 13:53:27

标签: types common-lisp type-theory

递归类型是一种具有基础和自身递归情况的类型。

我希望这能实现“类型列表”,即其conses只允许相同元素类型或nil的列表。

我尝试了以下定义:

(deftype list-of (a) `(or null
                          (cons ,a (list-of ,a))))

然而,由于编译器试图无限期地在列表上进行递归,因此这表示堆栈耗尽问题(至少在SBCL上)。是否可以定义这样的数据类型?

2 个答案:

答案 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不喜欢递归类型 - 另一个答案很好地解释了原因。但是如果你想坚持使用标准并仍然定义递归类型,隧道尽头就会有一个亮点:你可以定义任何函数来检查满意度。