Lisp 为什么(列表类型)等于CONS?

Lisp 为什么(列表类型)等于CONS?,lisp,common-lisp,Lisp,Common Lisp,我正在玩Common Lisp,刚刚意识到 (type-of (cons 1 2)) is CONS 及 然而,这两个元素显然不一样,因为所有“适当”的列表都必须是conses,第二个元素是列表 也就是说,当只有两个元素时,第二个元素是2,第一个元素是1,这两个元素都不是列表,但构造仍然称为cons 这变得更加混乱,因为 (print (list (cons 1 2) 3)) ; this is a ((1 . 2) 3), an improper list, but still cons

我正在玩Common Lisp,刚刚意识到

(type-of (cons 1 2)) is CONS

然而,这两个元素显然不一样,因为所有“适当”的列表都必须是conses,第二个元素是列表

也就是说,当只有两个元素时,第二个元素是2,第一个元素是1,这两个元素都不是列表,但构造仍然称为cons

这变得更加混乱,因为

(print (list (cons 1 2)  3)) ; this is a ((1 . 2) 3), an improper list, but still cons
(print (cons 1 (list 2 3))) ; this is a (1 2 3), a proper list, but still cons

(cons 1 (cons 2 3)) ; is not a proper list, but is a (1 2 . 3), but still cons...
所有这些都是缺点,但是为什么
(列表12)
不是一个列表呢?它不能是cons,因为cons和list必须是不同的类型,以便在确定它是否是正确列表的算法中进行区分(反过来,
(相等(列表12)(CONS1 2))
应该是正确的;没有这种区别,缺点和列表之间应该没有区别,只有缺点


有人能帮我理解为什么它说
(类型为(列表1-2))
是缺点,即使它显然是一个列表(否则我理解它是一个不合适的列表).

未在类型级别定义正确和不正确的列表。这将需要递归类型定义,这仅适用于具有类型的Lisp,在这种情况下,仍然不会返回复杂的类型说明符:

b、 返回的类型不涉及
eql
成员
满足

类型可以定义为
(或cons null)

类型
cons
null
构成类型
列表
的详尽分区

这意味着
nil
是一个列表,任何
cons
单元格都是一个列表。另请参见的定义

换言之:

(typep '(a b c) 'cons)
=> T
而且:

(typep '(a b c) 'list)
=> T
当然,这对于任何超类型都是正确的:

(typep '(a b c) 'sequence)
=> T

(typep '(a b c) 't)
=> T
函数的
类型返回最基本的类型,也就是说,可以认为没有其他子类型满足
类型p
(但请阅读给出实际定义的)

评论 我想澄清一下:

(cons 1 2)
…是一个列表,但它不能传递给需要正确列表(如
map
等)的函数。这在运行时进行检查,通常不会出现混淆,因为使用不正确列表的情况实际上非常罕见(例如,当您将
cons
单元格视为树时)。同样,通告清单也需要特殊处理。 为了检查列表是否正确,您只需要检查最后一个
cons
是否有
nil
作为其
cdr

还有,我看到你写了:

((1 . 2) 3) ; [...] an improper list

这里有两个元素的正确列表,其中第一个元素是不正确的列表,也称为虚线列表。

@coredump的答案是正确的,但了解其正确性的实际原因可能是有用的

首先,打字检查速度很快是非常理想的。因此,如果我说
(typep x'list)
,我希望它不必离开很长一段时间来进行检查

嗯,考虑一下一个合适的列表检查器应该是什么样子的。

(defun proper-list-p (x)
  (typecase x
    (null t)
    (cons (proper-list-p (rest x)))
    (t nil)))
(defun proper-list-p (x)
  (labels ((plp (thing seen)
             (typecase thing
               (null (values t nil))
               (cons
                (if (member thing seen)
                    (values nil t) ;or t t?
                  (plp (rest thing)
                       (cons thing seen))))
               (t (values nil nil)))))
    (plp x '())))
对于任何好的CL编译器来说,这都是一个循环(如果您可能需要处理初级编译器,它显然可以重写为显式循环)。但它是一个循环,与您正在检查的列表一样长,并且无法通过“typechecks should be quick”测试

实际上它没有进行更严重的测试:Type检查应该终止。考虑一个调用,如<代码>(PrimeList-P 1=(1。1))/Opto.OOPS。所以我们需要类似这样的东西,也许:

(defun proper-list-p (x)
  (typecase x
    (null t)
    (cons (proper-list-p (rest x)))
    (t nil)))
(defun proper-list-p (x)
  (labels ((plp (thing seen)
             (typecase thing
               (null (values t nil))
               (cons
                (if (member thing seen)
                    (values nil t) ;or t t?
                  (plp (rest thing)
                       (cons thing seen))))
               (t (values nil nil)))))
    (plp x '())))
那么,这将终止(并告诉您列表是否为循环):

(这个版本认为循环列表不合适:我认为另一个决定没那么有用,但在某种理论意义上可能同样合理。)

但是现在这个列表的长度是二次的,通过使用一个哈希表可以明显地改善这个效果,但是对于小的列表(哈希表很大)来说实现起来非常可笑

另一个原因是考虑表象类型和故意类型之间的差异:事物的代表性类型告诉你它是如何实现的,而有意的类型告诉你它是什么逻辑的,而且很容易看出,在一个具有可变数据结构的LISP中,代表性是非常困难的。(非空)列表的类型与cons列表的类型不同。下面是一个示例,说明了原因:

(defun make-list/last (length init)
  ;; return a list of length LENGTH, with each element being INIT,
  ;; and its last cons.
  (labels ((mlt (n list last)
             (cond ((zerop n)
                    (values list last))
                   ((null last)
                    (let ((c (cons init nil)))
                      (mlt (- n 1) c c)))
                   (t (mlt (- n 1) (cons init list) last)))))
    (mlt length '() '())))

(multiple-value-bind (list last) (make-list/last 10 3)
  (values
   (proper-list-p list)
   (progn
     (setf (cdr last) t)
     (proper-list-p list))
   (progn
     (setf (cdr (cdr list)) '(2 3))
     (proper-list-p list))))
最后一种形式的结果是:
t nil t
list
最初是一个合适的列表,然后它不是因为我摆弄了它的最终缺点,然后它又是因为我摆弄了一些中间缺点(现在,我对绑定到
last
的缺点所做的任何处理都不会对绑定到
list
的缺点产生影响)


如果你想使用任何类似链表的东西,那么就表征类型而言,跟踪某个东西是否是一个合适的列表是非常困难的。例如,
type of
,告诉你某个东西的表征类型,它只能是
cons
(或空列表的
null

这可能需要一段时间来消化O.O