我想删除两个给定列表中不包含的元素。
我正在尝试的代码是:
(defun remove_odd_ones(L1 L2)
(cond ((or (null L1) (null L2)) nil)
((dolist (x L2) (if (not(member x L1)) (remove x L2))))))
(remove_odd_ones '(a b c c e k) '(a b c p d d n))
我期待的是:(a b c c)
我收到的结果:NIL
我可以使用什么功能来保存dolist
会导致新列表?
答案 0 :(得分:0)
(remove_odd_ones '(a b c c e k) '(a b c p d d n))
我期待的是:(a b c c)
这称为“集合交集”:
Lisp> (intersection '(a b c c e k) '(a b c p d d n))
(A B C C)
我们如何能够(效率低下)自己做到这一点:
(defun our-intersection (l1 l2)
(mapcan (lambda (l1-item)
(if (member l1-item l2)
(list l1-item)))
l1))
映射l1
,收集l2
中的元素。
dolist
功能对此并不好,因为它不具备功能性/适用性;这是一个势在必行的循环小工具。如果您想使用dolist
收集结果列表,则必须在身体中执行操作,并借助其他变量:
(defun our-intersection (l1 l2)
(let ((out nil))
(dolist (l1-item l1 out)
(when (member l1-item l2)
(push l1-item out)))))
我们定义一个变量out
,它最初包含空列表。我们dolist
遍历l1
,依次将变量l1-item
设置为每个元素。如果l1-item
中出现l2
,则会将其推送到out
列表中:
Lisp> (our-intersection '(a b c c e k) '(a b c p d d n))
(C C B A)
dolist
至少让我们指定vi的第三个参数,表达式将生成表单的结果值:这就是我们放置对out
的引用的位置。
要按(A B C C)
顺序获取,我们可以使用nreverse
调用替换该引用,如下所示:
(dolist (l1-item l1 (nreverse out))
... )
这是列表上的Common Lisp过程编程中的常见模式:通过推送到新的本地列表来累积结果,然后在返回之前用nreverse
破坏性地反转它。 (这是安全的,因为列表是由程序代码在本地分配的:程序中没有任何其他内容知道列表,直到返回,因此破坏性重新安排不会影响任何其他功能或模块。)