首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >常见的Lisp:为什么删除-如果有这么多不同的删除-如果结合了setf?

常见的Lisp:为什么删除-如果有这么多不同的删除-如果结合了setf?
EN

Stack Overflow用户
提问于 2021-09-01 22:02:57
回答 1查看 76关注 0票数 1

我试图对集合执行破坏性操作,并注意到删除-if在SBCL和GNU CLISP中给出了令我惊讶的结果。

结合setf与删除-如果工作如预期,但我不明白为什么删除-如果不修改列表。

这是我的密码

代码语言:javascript
复制
(format t "trying weird delete-if problem~%")

(defun data ()
  (loop for x from 1 to 9 by 2 collect (cons x (1+ x))))

(defparameter *c1* (data))
(defparameter *c2* (data))

(format t "*c1* is now ~A~%" *c1*)
(format t "*c2* is now ~A~%" *c2*)

(delete-if
 (lambda (x)
   (progn
     (format t "trying to compare ~A ~A~%" x (equal x (cons 1 2)))
     (equal x (cons 1 2))))
 *c1*)

(format t "now trying remove-if~%")

(setf *c2* (remove-if
            (lambda (x)
              (progn
                (format t "trying to compare ~A ~A~%" x (equal x (cons 1 2)))
                (equal x (cons 1 2))))
            *c2*))

(format t "*c1* is now ~A~%" *c1*)
(format t "*c2* is now ~A~%" *c2*)
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-09-01 22:06:39

使用delete-if时,仍然必须将返回值赋值给保存原始列表的变量。这是因为破坏性删除可以更改列表的标识。

如果列表是非空的,则它的标识是它的第一个反单元格;如果它是空的,则它的标识是符号nil。如果破坏性删除操作生成的列表为nil (当前一个列表不是nil),或者生成前面有不同反单元格的列表,则标识已经更改。在这种情况下,必须将新的身份重新分配到列表所在的位置。

对列表的破坏性操作并不意味着我们不必因为列表具有引用语义而将任何东西分配给变量。因为Lisp列表是对单元格的未封装引用,或者是对nil的非封装引用,所以当我们破坏性地处理列表时,我们必须将列表存储的位置视为列表工作定义的一部分。破坏性列表不仅仅是对列表或nil的第一个单元格的引用,而是包含这样一个引用的placenil

当你以破坏性的方式对待一个列表时,你几乎总是希望只有一个地方。它是列表的一部分;破坏性列表通常不存在于两个地方。

我们可以使自己成为一个delete-if-place宏,用于从一个包含序列的位置中删除,类似于pushpop如何处理某个位置。

最简单的方法是使用define-modify-macro;不幸的是,宏希望使用筛选函数,这些函数将place的值作为最左边的参数,而delete-if则将其作为第二个参数。我们可以编写一个包装函数alt-delete-if,它反转这两个参数:

代码语言:javascript
复制
(defun alt-delete-if (pred seq &rest args)
  (apply #'delete-if seq pred args))

对于alt-delete-if,我们说的是(alt-delete-if list predicate ...)而不是(delete-if predicate list ...)

然后我们就可以:

代码语言:javascript
复制
(define-modify-macro delete-if-place (&rest args) alt-delete-if)

现在我们可以:

代码语言:javascript
复制
[1]> (defvar l (list 1 2 3 4 5 6))
L
[2]> (delete-if-place l #'oddp)
(2 4 6)

新名单更新了该地点:

代码语言:javascript
复制
[3]> l
(2 4 6)

也就是说,(delete-if-place place pred)执行(setf place (alt-delete-if pred place)),但place只计算一次。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69021032

复制
相关文章

相似问题

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