了解通用Lisp中的函数tailp

问题描述 投票:5回答:4

[当浏览Bert Burgemeister的“ Common Lisp快速参考”时,我偶然发现了tailp

首先,我误解了此函数的定义。我尝试过:

(tailp '(3 4 5) '(1 2 3 4 5))

但它返回了

NIL

CLTL2说,tailp为真iff第一个参数是具有现有(nthcdr n list)的任何n

(nthcdr 2 '(1 2 3 4 5))
;; (3 4 5)

我进一步尝试过:

(tailp '(3 4 5) '(1 2 3 4 5))
;; NIL - and I would expect: T following the definition above.

(tailp '() '(1 2 3 4 5))
;; T
(tailp '5  '(1 2 3 4 . 5))
;; T

直到我尝试过(然后才理解tailp寻找cdrl甚至共享相同的地址):

(defparameter l '(1 2 3 4 5 6))
(tailp (nthcdr 3 l) l)
;; T

但是随后我有下一个问题:

For what such a function is useful at all?

不是一个更有用的函数,它看起来子列表是否是列表的一部分? (或者看起来像列表的一部分,而不是它必须共享相同的地址?)

备注:

嗯,慢慢地,我开始理解,也许这是列表的eq部分的cdr ...种类...“给定列表cdr的任何eq派生词到第一个参数?”

但是也许有人可以向我解释这种测试在哪些情况下非常有用?

备注:

在与@Lassi here的长时间讨论中,我们发现:

切勿在通函列表上使用tailp

因为该行为未定义(在SBCL中已经出现问题)。因此,tailp用于非循环列表。

lisp common-lisp sbcl clisp
4个回答
6
投票

tailp的基本目的是检查是否存在共享的列表结构。这意味着cons单元格是否相同(这意味着EQL作为谓词)-不仅仅是cons单元格的内容。

也可以检查项目是否位于最后一个cdr中:

CL-USER 87 > (tailp t '(1 2 3 4 . t))
T

CL-USER 88 > (tailp nil '(1 2 3 4 . nil))
T

CL-USER 89 > (tailp nil '(1 2 3 4))
T

CL-USER 90 > (tailp #1="e" '(1 2 3 4 . #1#))
T

这是Common Lisp中很少使用的功能之一。


3
投票

[我很确定(tailp '(3 4 5) '(1 2 3 4 5))的答案可以是tnil,因为智能编译器可能会做(tailp '#1=#(3 4 5) '(1 2 . #1#))以减少内存占用。引用的内容是不可变的文字,所以为什么不两次使用相同的内存?

tailp的工作方式是:

(defparameter *tail* (list 3 4 5))
(defparameter *larger* (list* 1 2 *tail*))
(defparameter *replica* (copy-list *larger*))

(tailp *tail* *replica*) ; ==> nil
(tailp *tail* *larger*)  ; ==> t

由于copy-list将为其共享的列表中的每个元素创建新的缺点除了空列表和其他列表外,什么都没有。它是唯一的。

[*larger*已与*tail*制成共同尾号,因此(tailp *tail* *larger*)将为t

重要的是,将参数作为相同的对象进行比较。由于尾部不必是列表,因此它与eql进行比较。比较东西是否相同时,请使用equal,因此tailp的含义更为具体。它必须是等于(eq)或eql原子值的指针。

那么您在哪里使用呢?我在考虑一个功能性数据结构,您通常在其中重用共享结构。 tailp可能用于标识父级的子树。基本上,这是一个性能更高的版本:

(defun my-tailp (needle haystack)
  (cond ((eql needle haystack) t)
        ((atom haystack) nil)
        (t (my-tailp needle (cdr haystack)))))

3
投票

这里是tailp有用的情况:

(defun circular-list-p (l)
  (and (consp l)
       (tailp l (rest l))))

一些注意事项。

这在所有情况下都会终止:如果第一个参数不是第二个参数的末尾(即,不需要检查它的圆形性),则允许tailp在循环列表上不终止,但是如果第一个参数则必须终止。 秒尾。但是,如果列表是循环的,那正是我们在此处检查的内容,因此它将终止。 (我对此感到困惑了一段时间。)>

consp检查为(circular-list-p nil)为假:我认为这是实用的选择,尽管您的学徒可能会认为nil是圆形的。


1
投票

@@ Sylwester:

© www.soinside.com 2019 - 2024. All rights reserved.