首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >理解公共Lisp中的函数“尾部”

理解公共Lisp中的函数“尾部”
EN

Stack Overflow用户
提问于 2018-05-26 11:51:22
回答 4查看 380关注 0票数 6

在浏览伯特·伯格迈斯特( Bert Burgemeister )的“通用Lisp快速参考”()时,我偶然发现了tailp

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

代码语言:javascript
复制
(tailp '(3 4 5) '(1 2 3 4 5))

但它又回来了

代码语言:javascript
复制
NIL

CLTL2说,tailp是真实的,当且仅当,第一个论点是任何有现有n(nthcdr n list)

代码语言:javascript
复制
(nthcdr 2 '(1 2 3 4 5))
;; (3 4 5)

我进一步努力:

代码语言:javascript
复制
(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查找共享相同地址的cdr of l ):

代码语言:javascript
复制
(defparameter l '(1 2 3 4 5 6))
(tailp (nthcdr 3 l) l)
;; T

但后来我有了下一个问题:

代码语言:javascript
复制
For what such a function is useful at all?

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

备注:

啊,好吧,慢慢地,我开始明白了,也许这是一种对列表中的eq部分的cdr .有点..。“对于第一个参数,给定的列表( cdr-derivative of eq )有吗?”

但也许有人能解释一下,在什么情况下这样的测试是非常有用的?

备注:

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

从不在循环列表中使用!

因为行为是未定义的(在SBCL中已经有问题)。因此,tailp适用于非循环列表。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2018-05-26 20:14:59

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

还可以检查项目是否在最后一个cdr中。

代码语言:javascript
复制
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

这是公共Lisp中很少使用的函数之一。

票数 6
EN

Stack Overflow用户

发布于 2018-05-29 21:50:54

下面是tailp有用的一个例子:

代码语言:javascript
复制
(defun circular-list-p (l)
  (and (consp l)
       (tailp l (rest l))))

几个音符。

这在所有情况下都会终止:如果第一个参数不是第二个参数的尾(即不要求它检查循环性),则不允许tailp在循环列表中终止,但如果第一个参数是第二个参数的尾,则它必须终止。但是,如果列表是循环的,这正是我们在这里检查的,所以它终止了。(我对此感到困惑了一段时间)。

consp检查是错误的,所以(circular-list-p nil)是错误的:我认为这是一个实用的选择,尽管您可能会认为nil是循环的。

票数 4
EN

Stack Overflow用户

发布于 2018-05-28 18:57:33

我非常肯定(tailp '(3 4 5) '(1 2 3 4 5))的答案可以是tnil,因为智能编译器可能会做(tailp '#1=#(3 4 5) '(1 2 . #1#))来减少内存占用。引用的东西是不变的文字,那么为什么不使用相同的内存两次呢?

下面是tailp的工作方式:

代码语言:javascript
复制
(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可用于标识父级的子树。它基本上是一个更有表现力的版本:

代码语言:javascript
复制
(defun my-tailp (needle haystack)
  (cond ((eql needle haystack) t)
        ((atom haystack) nil)
        (t (my-tailp needle (cdr haystack)))))
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50542453

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档