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

Recursively remove n-th element of all sub-lists (with restrictions) in Common Lisp

我已经学习了几个星期的 Lisp(主要是递归)。到目前为止,我主要处理足够简单的函数来测试递归的基本知识(car、cdr、cond、remove、nth 等)。我偶然发现了一个我无法正确解决的简单问题。问题是:

Write a function remove-nth that takes two arguments:

(defun remove-nth (n l)

)

Where n is a non-negative integer and list is a list/atom/null. The function removes the n-th element (one-based indexing) of the original list and any-level sub-lists that it contains. The operation can be either destructive or non-destructive.

因此,例如:

(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)
)

此代码不起作用,因为它有时会从 same 列表中删除一个元素两次,例如呼叫:

(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) 删除了 两个 个元素,而不是一个。我认为我尝试处理递归调用的方式存在疏忽,但我尝试了许多不同的方法,但我无法使用任何方法。所以,我已经转向你们寻求帮助。

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

提前致谢!

看看这段代码,它应该能如您所愿地工作:

(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)))))

您的方法有 2 个问题:

  1. 在函数 r 中,您没有删除第 n 个元素 - 您正在删除 等于 列表中第 n 个元素的每个元素。因为列表 '(1 2 2 1) 被转换为 '(1 1) - 第二个元素是 2,所以每个 2 都被删除了。
  2. 在函数 remove-nth 中,您向下递归到第一个元素并一直递归到列表的尾部 - 由于第二次递归,您将删除更多应该删除的元素。这在列表 '(1 2 3 4) 的情况下很明显。调用了r之后就变成了'(1 3 4),然后你把它分解成car(等于1)和cdr(等于'(3 4) ).在递归到第二个元素之后,你再次删除第二个元素,你得到 '(3)。这会在 cons 之后为您提供列表 '(1 3)