Algorithm 公共Lisp中的线性递归列表差分函数

Algorithm 公共Lisp中的线性递归列表差分函数,algorithm,recursion,lisp,common-lisp,Algorithm,Recursion,Lisp,Common Lisp,我读教程是为了好玩,但最后一句话被他说的“练习:给出并和差的线性递归实现”(列表) 团结,没有汗水 差异,汗水 一次尝试看起来像这样 (defun list-diff (L1 L2) (cond ((null L1) L2) ((null (member (first L1) L2)) (cons (first L1) (list-diff (rest L1) L2))) (t (list-diff (rest L1) L2)) ) ) 现在,它返回L1中不在

我读教程是为了好玩,但最后一句话被他说的“练习:给出并和差的线性递归实现”(列表)

团结,没有汗水

差异,汗水

一次尝试看起来像这样

(defun list-diff (L1 L2)
  (cond
    ((null L1) L2) 
    ((null (member (first L1) L2)) (cons (first L1) (list-diff (rest L1) L2)))
    (t (list-diff (rest L1) L2))
  )
)
现在,它返回L1中不在L2中的所有元素,但它只返回L2中的所有元素(显然)。类似地,如果我将第3行中的L2改为“nil”,那么它只返回所有不在L2中的L1,但不返回任何L2

我尝试的变通方法看起来不是递归的,当它们是递归的时候,我最终会得到堆栈溢出(就像我尝试在某个地方调用(list diff L2 L1)

他的任何其他练习,例如列表交叉,只需要遍历L1的元素。在这里,我想从L2中删除关键元素,或者运行(list diff L2 L1),然后合并这两个元素的结果,但这不再是真正的线性递归

想法

(不是家庭作业,真的。我只是觉得我应该试着看一些口齿不清的东西来取乐。)

编辑:根据响应正确执行此操作的函数是:

(defun list-diff (L1 L2)
  (cond
    ((null L1) nil)
    ((null (member (first L1) L2)) (cons (first L1) (list-diff (rest L1) L2)))
    (t (list-diff (rest L1) L2))
  )
)
操作L1\L2被定义为所有元素e,使得e在L1中,但e不在L2中。所以在我看来,你的第二次尝试实际上是正确的:

类似地,如果我在第二行中更改L2 3到“nil”,则返回全部 不在L2中,但不在 L2

看起来您正在尝试计算,虽然我不清楚这是否是练习要求的


如果你想聪明一点,你可以把第三个列表传递到函数中作为累加器。当L1有元素时,当
(null(成员(第一个L1)L2))
时,将第一个元素推入累加器(并递归调用)。当L1为空时,根据累加器列表检查L2的元素,执行相同的操作。当L1和L2为空时,返回累加器列表。

在Lisp中,这是“设置差异”的定义:

set-difference list1 list2 &key (test #'eql) test-not (key #'identity)
   Function
   Returns a list of elements of list1 that do not appear in list2. 
这是您修改后的实现:

(defun list-diff (L1 L2)
  (cond
    ((null L1) L1)
    ((member (first L1) L2) (list-diff (rest L1) L2))
    (t (cons (first l1) (list-diff (rest l1) l2)))))

嗯,是的,只返回l1是第一个子句。风格:在第二个子句中编写测试,不调用null,并交换第二和第三个子句的结果部分。稍微简化了代码。样式:不要使用C格式。将括号放在函数末尾后面。使用自动缩进、括号突出显示等方法来正确地获取代码结构。Rainer--谢谢。也许你可以把你所做的修改贴出来,这样我就可以看到它们了?哦,好的。那么,我对集差的定义是错误的。我在考虑对称差分。是的,我的第二个版本做得很好。我曾考虑过使用蓄能器,但在他做那项练习时还没有介绍过。