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