在Common Lisp中递归删除所有子列表的第n个元素(有限制)

时间:2015-04-19 14:15:38

标签: list common-lisp

我在研究Lisp(主要是递归)的几周内。到目前为止,我一直在处理足够简单的函数来测试递归的基本知识(car,cdr,cond,remove,nth等)。我偶然发现了一个我无法做对的简单问题。问题是:

  

编写一个带有两个参数的函数remove-nth:

(defun remove-nth (n l)

)
  

其中n是非负整数,list是list / atom / null。该函数删除原始列表的第n个元素(基于一个索引)以及它包含的任何级别子列表。该操作可以是破坏性的,也可以是非破坏性的。

所以,例如:

(remove-nth 3 '((1 2 3 4) ((1 3 5) 3 1) (1 2 2 1))) --> ((1 2 4) ((1 3) 3))

我尝试过的事情:

(defun remove-nth (n L)
   (cond
       ((null L) nil)
       ((listp L)
            (cons (remove-nth n (car (r n L))) (remove-nth n (cdr (r n L)))))
       (t L)      
   )
)

(defun r (n L)   
    (remove (nth (- n 1) L) L)
)

此代码不起作用,因为它有时会从相同的列表中删除两次元素,例如主叫:

(remove-nth 2 '((1 2 3 4) ((1 3 5) 3 1) (1 2 2 1))) 

应该输出

((1 3 4) (1 2 1))

但在我的情况下,它输出:

((1 3) (1 1)) 

即。子列表(1 2 2 1)删除了两个元素而不是一个。我认为我试图处理递归调用的方式存在疏忽,但我尝试了很多不同的方法,但我无法工作。所以,我已经向你们求助了。

我不是要求代码本身,而是要求解释,或暗示如何改进我的方法。

提前致谢!

1 个答案:

答案 0 :(得分:1)

看看这段代码,它应该可以正常工作:

(defun remove-nth (n L)
   (cond
       ((null L) nil)
       ((listp L)
        (map 'list (lambda (l) (remove-nth n l)) (r n L)))
       (t L)))

(defun r (n L)                                     
   (if (or (= n 1) (null L)) 
      (cdr L) 
      (cons (car L) (r (1- n) (cdr L)))))

您的方法存在两个问题:

  1. 在函数r中,您没有删除第n个元素 - 您正在将每个与相等的元素移除到列表的第n个元素。因此,列表'(1 2 2 1)已转换为'(1 1) - 第二个元素为2,因此每个2都会被删除。
  2. 在函数remove-nth中,您将向下递归到第一个元素并直接进入列表的尾部 - 由于第二个递归,您将删除更多应该使用的元素。在列表'(1 2 3 4)的情况下,这是显而易见的。调用'(1 3 4)后,它会变为r,然后您将其分解为car(等于1)和cdr(等于'(3 4) )。在递归到第二个元素之后,您再次删除第二个元素,然后获得'(3)。在cons之后,这会为您提供列表'(1 3)