Search code examples
recursioncommon-lisp

Single test recursion - how to create the "no result found" return value?


I am working on a recursive function that looks for the first odd number in a list. It works as expected.

But when there is NO odd number in a list, it returns an error. Id like to control this error with some sort of message that says "no odd values found".

Single Test Recursion Function

(defun find-first-odd (x)
  (cond ((oddp (first x)) (first x))
        (t (find-first-odd (rest x)))))


(find-first-odd '(2 2 10 3 4 6 4)) ; => 3

(find-first-odd '(2 2 10 4 6 4)) ;=> value nil is not an integer


(find-first-odd '(2 2 10 4 6 4 . 2)) ;=> 2 is not type list



Solution

  • You need to test for the list being empty and do the appropriate thing. In general any function which searches a list recursively needs a termination test, and thus looks something like:

    (defun search-list-for (l ...)
      (cond ((null l)
             ...)
            (<(first l) is what we're after>
             ...)
            (t
             (search-list-for (rest l) ...))))
    

    and in this case:

    (defun find-first-odd (l)
      (cond
       ((null l)
        nil)                                ;or whatever
       ((oddp (first l))
        (first l))
       (t
        (find-first-odd (rest l)))))
    

    You are lucky that, for your particular function, while (first '()) is perfectly legal in CL, it is not a number and so you get an error. In general you'll get failure to terminate:

    (defun contains-thing-p/broken (l thing)
      (cond
       ((eql (first l) thing)
        t)
       (t
        (contains-thing-p/broken (rest l) thing))))
    

    will not terminate if thing is not in the list, and must be written using the above skeleton instead:

    (defun contains-thing-p (l thing)
      (cond
       ((null l)
        nil)
       ((eql (first l) thing)
        t)
       (t
        (contains-thing-p (rest l) thing))))