First, lets clean up the formatting a bit to make things easier to read. Note that leaving hanging parentheses on their own lines is very un-idiomatic in lisps; this isn’t a curly-braces language, after all:
(defun removeNILMost (L)
(cond ((NULL L) NIL)
((listp (car L))
(OR (removeNILMost (car L))
(removeNILMost (cdr L))))
((not (eq nil (car L)))
(cons (car L)
(removeNILMost (cdr L))))
(T
(removeNILMost (cdr L)))))
Now, why is or
being used to combine the results of two recursive calls to removeNILMost
? Because or
is short-circuiting, only the first result is returned when both are non-empty lists in the pair of calls:
(OR (removeNILMost (car L))
(removeNILMost (cdr L))))
This is not the desired outcome, and is the reason that the final z
is missing with (removeNILMost '(nil (nil) (x nil y) (nil nil) z))
.
Now, since removeNILMost
returns a list, append
is the appropriate function for combining these results to give OP result of (removeNILMost '(X NIL (Y NIL Z) NIL))
–> (X Y Z)
. Here the goal must be to both flatten the input list and to remove all nil
s from it. This change would “fix” OP posted code:
(append (removeNILMost (car L))
(removeNILMost (cdr L))))
If the goal is only to remove nil
s from the input, then the tree structure of the input list should be preserved. The examples do not show this, but I suspect that the correct implementation would give:
CL-USER> (removeNILMost '(X NIL (Y NIL Z) NIL))
(X (Y Z))
To preserve the structure of the list, a conditional can check whether the first result is nil
or not before consing to the second result:
(defun removeNILMost (L)
(cond ((NULL L) NIL)
((listp (car L))
(let ((left (removeNILMost (car L)))
(right (removeNILMost (cdr L))))
(if left
(cons left right)
right)))
((not (eq nil (car L)))
(cons (car L)
(removeNILMost (cdr L))))
(T
(removeNILMost (cdr L)))))
The following code cleans up the above a bit; note that kebab-case
is preferred to variations on camelCase
in lisps. The tests have been reorganized a bit to make the logic more clear.
(defun remove-all-nils (L)
(cond ((null L) '()) ; empty list input remains empty
((null (car L)) ; if the first element is NIL, just move on
(remove-all-nils (cdr L)))
((listp (car L)) ; if the first element is a list...
(let ((left (remove-all-nils (car L)))
(right (remove-all-nils (cdr L))))
(if left ; conditionally CONS the results together
(cons left right)
right)))
(t ; otherwise the first element is not a list
(cons (car L) ; so CONS it to the result of processing the remainder
(remove-all-nils (cdr L))))))
Here are some sample interactions:
CL-USER> (remove-all-nils '(nil x nil nil y nil z))
(X Y Z)
CL-USER> (remove-all-nils '(x nil (y nil z) nil))
(X (Y Z))
CL-USER> (remove-all-nils '(nil (nil) (x nil y) (nil nil) z))
((X Y) Z)
CL-USER> (remove-all-nils '(nil a (b (c nil (nil) d) e (nil f g (h nil i (nil j (nil nil) k) l nil m) n ) o p q) r nil))
(A (B (C D) E (F G (H I (J K) L M) N) O P Q) R)
CL-USER> (remove-all-nils '(nil ((nil (nil)) (nil (nil (nil (nil nil)) nil))) nil))
NIL
CLICK HERE to find out more related problems solutions.