Search code examples
recursionlispcommon-lispnested-lists

How to remove all numbers from a list in Lisp


I am learning Lisp and have a problem where I have to remove all the numbers from a list, which also has nested lists. For example:

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

will output:

(A (B) C D)

I am not allowed to use loop, if, or mapcan. I am to use basic operators and cond statements. This is my current code:

(defun REMOVE-NUM (L)
  (cond
   ((null L) nil)

   ((numberp (car L)) (REMOVE-NUM (cdr L)))

   (t (cons (car L) (REMOVE-NUM (cdr L))))   
)

My current code can remove the numeric atoms without nested lists, but once nested lists are involved, it lists out the nested list, without actually processing it. I know that this is an issue with my logic behind the recursion, but I for some reason cannot see it. My instinct is to change the last line to:

(t (cons (REMOVE-NUM (car  L) (REMOVE-NUM (cdr L))))

another recursive call in the last line, but doing so gives an error because it tries to take car of an atom and pass it as a list to my function which can only take lists. I thought of maybe changing the base cases, but I can't think of a way to do so since numberp has to be condition used. Where is my recursion going wrong/what is it missing?


Solution

  • Isn't it obvious? Separate atom (which could be unified with the null case actually) and the listp (which could then be simply t) cases. You'll need to accept that your input isn't necessarily a list if you want recursion all the way, or else you need to peek at the cdr and make sure it's listp before you recurse. Throw in a cons or two in your test case.

    (REMOVE-NUM '(2 A (3 B) C (D . 7) (8 . E) F))

    If you don't need to handle cons cells in your lists, simply

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

    Demo: https://ideone.com/evidN7