I am attempting to write a small recursive program that tests a list and returns t if every element is an atom. The problem I am having is that when the function receives an empty list, it returns t instead of the desired result of nil. I cannot come up with a way to 开发者_JS百科have it return nil for an initially empty list and still function properly in a recursive manner.
(defun only-atoms (in)
(if (null in)
t
(and (atom (first in)) (only-atoms (cdr in)) )
)
)
The function can be implemented without recursion using e.g. every
, as in:
(defun only-atoms (list)
(and list (every #'atom list)))
When it comes to your stated problem that the function returns T
instead of the desired result of NIL
when the function is called with an empty list:
Your recursive implementation explicitly returns
T
if(null in)
is true, which explains your finding. Simply change it to the desired valueNIL
. Consider changing theif
construct toand
.Only make the recursive call when the list has more than one item. A well placed test for
(rest in)
will do. Provide a true value instead of making the recursive call if the list is at its last item.Carefully locate the
only-atoms
call to ensure that the function can be tail-recursive.
For example:
(defun only-atoms (list)
(and list
(atom (first list))
(or (null (rest list))
(only-atoms (rest list)))))
Use COND, which allows you to test for several cases:
- empty list -> nil
- one element list -> atom? ; hint: don't use LENGTH
- else -> atom? for first element and recursive call for rest elements
The empty list does fulfill the condition that every element is an atom! Your requirement that it should contain at least one element is an additional requirement.
The simplest way to express "every element of the list is an atom" is (every #'atom list)
. You can combine it with your additional requirement with and
.
If you insist on doing it recursively in SICP-style, separate your requirements:
(defun not-empty-and-all-atoms (list)
(and list
(all-atoms list)))
(defun all-atoms (list)
(if (endp list)
t
(and (atom (first list))
(all-atoms (rest list)))))
This solution works correctly:
(defun lat-p (lst)
(labels ((lat-p* (lst)
(cond
((null lst) t)
((atom (car lst)) (lat-p* (cdr lst)))
(t nil))))
(if lst
(lat-p* lst)
nil)))
However a much more elegant solution(with no recursion) would be:
(defun lat-p (lst)
(and lst (every #'atom lst)))
You can split your function in two, and provide the initial nil
screening before you enter recursion. The following code is one way to do so (I tried to keep it as close to provided code as possible):
(defun only-atoms (in)
(defun only-atoms-iter (in)
(if (null in)
t
(and (atom (first in)) (only-atoms-iter (cdr in)))))
(unless (null in)
(only-atoms-iter in)))
This is also a good opportunity to make your function tail recursive:
(defun only-atoms (in)
(defun only-atoms-iter (in state)
(if (null in)
state
(only-atoms-iter (cdr in) (and state (atom (first in))))))
(unless (null in)
(only-atoms-iter in t)))
精彩评论