【问题标题】:Why is (type-of list) equal to CONS?为什么(列表类型)等于CONS?
【发布时间】:2016-11-12 08:25:45
【问题描述】:

我正在玩 Common Lisp 并且刚刚意识到

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

(type-of (list 1 2)) is also CONS

但是这两者显然不一样,因为所有“正确”的列表都必须与第二个元素为列表相结合。

也就是说,当只有两个元素时,第二个元素为 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...

都是缺点,但为什么(list 1 2) 不是一个列表?它不能是一个缺点,因为 cons 和 list 必须是不同的类型,以便在确定它是否是正确列表的算法中被区分(反过来,(equal (list 1 2) (cons 1 2)) 应该是真的;没有这种歧视,缺点和列表之间应该没有区别,只有缺点。

有人可以帮我理解为什么它说(type-of (list 1 2)) 是缺点,即使它显然是一个列表(否则它会是我理解的不正确的列表)。

【问题讨论】:

    标签: lisp common-lisp


    【解决方案1】:

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

    b.返回的类型不涉及andeqlmembernotorsatisfiesvalues

    list 类型可以定义为(or cons null)

    consnull 类型构成了 list 类型的详尽分区。

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

    换句话说:

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

    还有:

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

    当然,任何超类型都是如此:

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

    type-of 函数返回最基本的类型,即cons,可以将其视为没有其他子类型满足typep 的类型(但请阅读给出实际定义的specification)。

    备注

    澄清一下:

    (cons 1 2)
    

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

    另外,我看到你写道:

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

    这里有两个元素的正确列表,其中第一个是不正确的列表,也就是点列表。

    【讨论】:

    • 这可能需要一段时间来消化 O.o
    【解决方案2】:

    @coredump 的答案是正确的,但了解实际原因可能很有用为什么它是正确的。

    首先,类型检查很快是非常可取的。所以如果我说(typep x 'list),我希望它不必离开很长时间来进行检查。

    好吧,考虑一下合适的列表检查器应该是什么样子。可能是这样的:

    (defun proper-list-p (x)
      (typecase x
        (null t)
        (cons (proper-list-p (rest x)))
        (t nil)))
    

    对于任何好的 CL 编译器,这都是一个循环(如果您可能需要处理初级编译器,它显然可以重写为显式循环)。但它是一个循环,与您正在检查的列表一样长,这会导致“类型检查应该快速”测试失败。

    事实上,它没有通过更严重的测试:类型检查应该终止。考虑像@​​987654323@ 这样的电话。哎呀。所以我们需要这样的东西,也许:

    (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 '())))
    

    好吧,这将终止(并告诉你列表是否是循环的):

    > (proper-list-p '#1=(1 . #1#))
    nil
    t
    

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

    但这现在是列表长度中的二次。这可以通过以显而易见的方式使用哈希表来做得更好,但是对于小列表(哈希表很大),实现是可笑的。

    另一个原因是考虑representational类型和intentional类型之间的区别:某事物的表示类型告诉你它是如何实现的,而意图类型告诉你什么它在逻辑上是。很容易看出,在具有可变数据结构的 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 tlist 最初是一个正确的列表,然后不是因为我摆弄了它的最终缺点,然后又是因为我摆弄了一些中间缺点(和现在,无论我对绑定到 last 的 cons 做什么,都不会影响绑定到 list 的 cons。

    如果您想使用任何类似于链表的东西,那么就表示类型而言,要跟踪某物是否是正确的列表将非常困难。例如,type-of 会告诉您某事物的表示类型,它只能是 cons(或 null 用于空列表)。

    【讨论】:

      猜你喜欢
      • 2011-09-05
      • 1970-01-01
      • 2016-06-28
      • 2020-08-22
      • 1970-01-01
      • 2018-03-17
      • 1970-01-01
      • 2021-11-11
      • 1970-01-01
      相关资源
      最近更新 更多