如何在 Lisp 中删除列表中的所有数字

How to remove all numbers from a list in Lisp

提问人:William Young 提问时间:10/1/2023 最后编辑:Rainer JoswigWilliam Young 更新时间:10/3/2023 访问量:111

问:

我正在学习 Lisp,并且遇到了一个问题,我必须从列表中删除所有数字,该列表也有嵌套列表。例如:

(REMOVE-NUM '(2 A (3 B) C D))

将输出:

(A (B) C D)

我不被允许使用 、 或 。我将使用基本的运算符和语句。这是我当前的代码:loopifmapcancond

(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 作为条件。我的递归哪里出了问题/缺少什么?

递归 lisp common-lisp 嵌套列表

评论

1赞 Martin Půda 10/1/2023
它是否也适用于配对?(例如,cons(remove-num '((A . 1) (B . 2))))
1赞 Rainer Joswig 10/2/2023
函数缺少右括号。REMOVE-NUM
2赞 Kaz 10/2/2023
我从来没有看到 Python 或 Javascript 作业问题说,“我们不允许使用这个或那个”。为什么 Lisp 会得到那些糟糕的老师?这是一种愤世嫉俗的观点,它说,“在这门课之后,你实际上不会用Lisp做任何事情,所以学习这门语言所提供的一切是没有意义的。只是概念,我们将使用一个受限制的子集。如果可以的话,找另一个班级。
0赞 William Young 10/2/2023
@MartinPůda 提示没有说明任何关于配对的内容。它指定函数将仅接收原子列表和/或包含原子的嵌套列表。cons
0赞 tripleee 10/3/2023
@Kaz 教授可能更希望学生在被允许使用有效隐藏这个实现层的高阶函数之前学习低级列表架构。

答:

2赞 tripleee 10/1/2023 #1

这不是很明显吗?单独的(实际上可以与案例统一)和(然后可以简单地)案例。如果你想要一直递归,你需要接受你的输入不一定是一个列表,否则你需要偷看并确保它是在你递归之前。在测试用例中加入一两个。atomnulllistptcdrlistpcons

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

演示:https://ideone.com/evidN7

评论

0赞 William Young 10/2/2023
感谢您的回复!我有点困惑;你是说用 和 重写函数吗?提示确实说该函数只会将列表作为输入,并且我必须始终使用递归。atomplistp
0赞 tripleee 10/2/2023
但是当你递归时,你不知道它是否是一个列表。cdr
0赞 William Young 10/3/2023
谢谢你的帮助。我已经去了办公时间并澄清了这一点。我的教授的回答略有不同,因为它涉及使用两次,一次打开和一次打开。您的解决方案更容易遵循。numberpcar LL
1赞 ad absurdum 10/2/2023 #2

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 满足需求。ifcondcondatomandlet

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

评论

0赞 William Young 10/2/2023
感谢您的回复。很遗憾,我不被允许使用.我将尝试将其转换为允许使用的内容。if
1赞 ad absurdum 10/2/2023
@WilliamYoung -- 我只是补充一些关于这一点的评论。采用基本思想并使用您的任何要求开发解决方案应该非常简单。
1赞 ignis volens 10/2/2023 #3

您的代码(模格式和末尾缺少一些紧密的段落)几乎是正确的,但缺少一个案例。这是伪代码,其中包括该情况以及另一个捕获错误的情况

要从可能嵌套的列表中删除数字,请 l

  • 根本不是列表吗?这是一个错误;
  • L是空列表吗?如果是,结果是空列表;
  • L的车是名单吗?如果是,则结果是一个列表,方法是将从 L 的汽车中删除数字的结果与从 L 的 CDR 中删除数字的结果相加得出的列表;
  • L的车是数字吗?如果是,则结果是从 L 的 CDR 中删除数字的结果;
  • 否则,结果是将 L 的 car 与从 L 的 CDR 中删除数字的结果相融合的结果。

对于第一种情况,“l 不是列表”情况,您可以使用 发出错误信号。如果你的导师说你不允许检查函数参数中的错误,就像这样,用强硬的话来对待他们。(error "...")