提问人:William Young 提问时间:10/1/2023 最后编辑:Rainer JoswigWilliam Young 更新时间:10/3/2023 访问量:111
如何在 Lisp 中删除列表中的所有数字
How to remove all numbers from a list in Lisp
问:
我正在学习 Lisp,并且遇到了一个问题,我必须从列表中删除所有数字,该列表也有嵌套列表。例如:
(REMOVE-NUM '(2 A (3 B) C D))
将输出:
(A (B) C D)
我不被允许使用 、 或 。我将使用基本的运算符和语句。这是我当前的代码:loop
if
mapcan
cond
(defun REMOVE-NUM (L)
(cond
((null L) nil)
((numberp (car L)) (REMOVE-NUM (cdr L)))
(t (cons (car L) (REMOVE-NUM (cdr L))))
)
我当前的代码可以删除没有嵌套列表的数字原子,但是一旦涉及嵌套列表,它就会列出嵌套列表,而不会实际处理它。我知道这是我递归背后的逻辑问题,但由于某种原因我看不到它。我的直觉是将最后一行更改为:
(t (cons (REMOVE-NUM (car L) (REMOVE-NUM (cdr L))))
最后一行中的另一个递归调用,但这样做会给出错误,因为它试图获取原子的 car 并将其作为列表传递给我的函数,该函数只能接受列表。我想过也许可以更改基本情况,但我想不出一种方法来做到这一点,因为必须使用 numberp 作为条件。我的递归哪里出了问题/缺少什么?
答:
这不是很明显吗?单独的(实际上可以与案例统一)和(然后可以简单地)案例。如果你想要一直递归,你需要接受你的输入不一定是一个列表,否则你需要偷看并确保它是在你递归之前。在测试用例中加入一两个。atom
null
listp
t
cdr
listp
cons
(REMOVE-NUM '(2 A (3 B) C (D . 7) (8 .E) F))
如果您不需要处理列表中的单元格,只需cons
(defun REMOVE-NUM (L)
(cond
((atom L) L)
((numberp (car L)) (REMOVE-NUM (cdr L)))
(t (cons (REMOVE-NUM (car L))
(REMOVE-NUM (cdr L)) ))))
评论
atomp
listp
cdr
numberp
car L
L
OP 代码适用于包含原子的列表,但不适用于同时包含更多原子列表的列表。
考虑以下情况: .OP 代码检查:(remove-number '((a 1 2) b 3))
- 输入是空列表吗?不。
- 输入的第一个元素是数字吗?不。
- 好的,那么,保留输入的第一个元素并继续。
但是 OP 代码无法检查重要情况:输入本身的第一个元素是列表吗?
OP 本能地将最后一行更改为:.这是一个正确方向的想法,但它不会有效,因为代码没有检查的第一个元素是否是列表。(t (cons (REMOVE-NUM (car L) (REMOVE-NUM (cdr L))))
L
相反,代码应检查:
- 输入是空列表吗?如果是,只需返回一个空列表。
- 输入的第一个元素是列表吗?如果是,则将从此子列表中删除数字的结果与从其余输入列表中删除数字的结果相符,并返回结果。
- 输入的第一个元素是数字吗?如果是,只需从剩余的输入列表中删除数字并返回结果即可。
- 最后,第一个元素不是列表,也不是数字,因此将第一个元素放在从剩余输入列表中删除数字的结果上。
也就是说:当函数处理输入列表的元素时,它必须能够区分和处理列表元素、数字元素和其他元素。
下面是这个函数的一个实现,但OP最好在阅读上面的内容后先尝试自己编写这个。当然,还有其他方法可以写出来。这种实现似乎不符合 OP 要求,因为它使用 而不是 ,并且不清楚允许使用哪些“基本运算符”。无论如何,使用(例如,,,)编写类似的东西是相对简单的,同时避免它是否有助于 OP 满足需求。if
cond
cond
atom
and
let
(defun remove-numbers (xss)
(if (null xss) '()
(let ((x (car xss))
(remainder (cdr xss)))
(if (listp x)
(cons (remove-numbers x) (remove-numbers remainder))
(if (numberp x)
(remove-numbers remainder)
(cons x (remove-numbers remainder)))))))
CL-USER> (remove-numbers '(2 a (3 b) c d))
(A (B) C D)
CL-USER> (remove-numbers '(1 a ((2 3 b c) d 4) 5 6))
(A ((B C) D))
评论
if
您的代码(模格式和末尾缺少一些紧密的段落)几乎是正确的,但缺少一个案例。这是伪代码,其中包括该情况以及另一个捕获错误的情况
要从可能嵌套的列表中删除数字,请 l:
- 我根本不是列表吗?这是一个错误;
- L是空列表吗?如果是,结果是空列表;
- L的车是名单吗?如果是,则结果是一个列表,方法是将从 L 的汽车中删除数字的结果与从 L 的 CDR 中删除数字的结果相加得出的列表;
- L的车是数字吗?如果是,则结果是从 L 的 CDR 中删除数字的结果;
- 否则,结果是将 L 的 car 与从 L 的 CDR 中删除数字的结果相融合的结果。
对于第一种情况,“l 不是列表”情况,您可以使用 发出错误信号。如果你的导师说你不允许检查函数参数中的错误,就像这样,用强硬的话来对待他们。(error "...")
评论
cons
(remove-num '((A . 1) (B . 2)))
)REMOVE-NUM
cons