0

This function here:

(defun test (a)
               (if (equal a nil) 0)
               (if (listp (car a)) (print "a")
                 (print "b"))
               (test (cdr a))
               )

I want it to return 0 if a is nil which is the base case. then if the element in the front of the list is a list, print the letter a otherwise print b then call function again. Why doesn't the base case prevent the infinite loop?

1
  • In implementations that perform tail-call optimization, this would just be an infinite loop. Commented Feb 18, 2014 at 15:05

3 Answers 3

3

Your code ends up with a stack overflow because you recurse into test regardless of the result of the nil-check.

(defun test (a)
  (if (equal a nil) 0)  ; <-- problem is partly here...
  (if (listp (car a))
      (print "a")
      (print "b"))
  (test (cdr a)))       ; <-- ...and partly down here

Even if (equal a nil) evaluates to T and the surrounding if therefore evaluates to 0, you basically ignore this result, because the next step you do is checking if a is a list regardless of the outcome of the previous nil-check. Eventually you call test again without taking the result of your comparisons into consideration.

Keep in mind that the result of a function in Lisp is the result of the last evaluated expression, meaning that if you want your function to return 0 you have to make 0 the last evaluated expression.

The following code behaves as you specified:

(defun test (a)
  (if (null a)
      0
      (progn
        (if (listp (car a))
            (print "a")
            (print "b"))
        (test (cdr a)))))
  • Note that you can use null to do a nil-check.
  • If a is not nil, then and only then, a is examined further. For this purpose progn lets you evaluate a sequence of expressions and eventually evaluates to the result of its last expression.
Sign up to request clarification or add additional context in comments.

5 Comments

I'd almost upvote you, but your indentation for the if is off. Standard indentation is to align the "then" and "else" at the same indentation as the test.
I haven't been doing anything Lisp-related for quite a while, but I've seen it being done both ways. Is there something like a "definitive" style guide?
I tend to use Riastradh's Lisp Style Rules, though it's not CL-specific. But even the first Google hit for "common lisp indentation" agrees with this.
Thanks for your recommendation. I agree and will fix this.
Okay, so this is what I wanted to do... if a nil return 0 else if a is a list print "a" else if a is not a list print "b" return function with cdr a
3

The other answers have explained your problem very well; just 2 notes:

cond

At least one style guide advises againt the use of if and prefers cond; this would have avoided the "fall-through problem":

(defun test (a)
  (cond
   ((equal a nil) 0)
   (t (if (listp (car a)) 
        (print "a")
        (print "b"))
      (test (cdr a)))))

return

You can return early from a function; your code would have worked with a return-from clause:

(defun test (a)
  (if (equal a nil) (return-from test 0))
  (if (listp (car a)) 
    (print "a")
    (print "b"))
  (test (cdr a)))

Comments

1

Because your base case is still followed by the printing and recursion. It didn't return straight afterwards.

Perhaps you wanted this:

(defun test (a)
  (if (null a)
      0
      (progn (if (listp (car a))
                 (print "a")
                 (print "b"))
             (test (cdr a)))))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.