Page 1 of 2

Why does taking out nil break this code segment?

Posted: Thu Jan 21, 2010 1:26 pm
by yougene

Code: Select all

(defun our-member (obj lst)
  (if (null lst)
      nil
      (if (eql (car lst) obj)
          lst
          (our-member obj (cdr lst)))))
Taking out the nil breaks the code. Why?

Re: Why does taking out nil break this code segment?

Posted: Thu Jan 21, 2010 2:51 pm
by ramarren
Generally randomly reversing the meaning of conditional expressions will break things. If you do not understand what IF does then I would suggest going back to the basics.

Re: Why does taking out nil break this code segment?

Posted: Thu Jan 21, 2010 3:51 pm
by gugamilare
yougene wrote:

Code: Select all

(defun our-member (obj lst)
  (if (null lst)
      nil
      (if (eql (car lst) obj)
          lst
          (our-member obj (cdr lst)))))
Taking out the nil breaks the code. Why?
Nil is the default value returned if the list is empty, that is why it is there. If you remove it, the last if is evaluated only when the list is empty, which won't work.

Re: Why does taking out nil break this code segment?

Posted: Fri Jan 22, 2010 1:01 pm
by yougene
I guess my question is why are the two if statements nested?

If the first IF is true and it returns nil how can it go on to evaluate the second IF?

Re: Why does taking out nil break this code segment?

Posted: Fri Jan 22, 2010 1:07 pm
by yougene
I think I found my answer

(if < condition-form >
< then-form >
< else-form >)


Does this mean that the last line is always assumed to be the command for "else"?

Re: Why does taking out nil break this code segment?

Posted: Fri Jan 22, 2010 1:48 pm
by ramarren
yougene wrote:Does this mean that the last line is always assumed to be the command for "else"?
It is not a line, it is a form, and not a command but a possibly side effecting expression. This is not just semantics, understanding the meaning of these words is crucial to understanding Lisp. And no, if the IF form has only two arguments then the second one, which will in that case be last, would be a "then" expression, and the else branch would be an implicit NIL. When you removed the NIL in your original post you effectively exchanged the branches.

Seriously, questions at this level are best answered as part of comprehensive text like the one I linked earlier or Practical Common Lisp. Guessing at syntax and semantics of a language rarely ends well.

Re: Why does taking out nil break this code segment?

Posted: Sat Jan 23, 2010 4:15 pm
by hewih
in if you can only have 1 form for then and 1 form for else. the other way around, how would Lisp know which form belongs to then and which to else

Code: Select all

(if (null lst)
   (format T "then")
   (format T "else")
)

(if (null lst)
   (format T "then")
   (format T "else")
   (format T "umm...?") ; <= not working!!!
)
if you only need the then or else, use when or unless

Code: Select all

(when (null lst)
    (format T "statement1")
    (format T "statement2")
    (format T "statement3")
)
if you need more statements in then or else, use progn

Code: Select all

(if (null lst)
   (progn ; encapsulates the whole then block
      (format T "statement1")
      (format T "statement2")
      (format T "statement3")
      )
   (format T "else")
)

Re: Why does taking out nil break this code segment?

Posted: Sat Jan 23, 2010 4:46 pm
by ramarren
hewih wrote:in if you can only have 1 form for then and 1 form for else. the other way around, how would Lisp know which form belongs to then and which to else
That is true for Common Lisp, but for example in Emacs Lisp all forms after the second form an implicit PROGN for the else clause. Some people even use an IF* macro like this in CL, although I don't really think this is all that useful.
hewih wrote:if you need more statements in then or else, use progn
Even better, use COND. This is of course subjective, but I don't think I am alone in thinking that is clearer, even for just two branches.

Re: Why does taking out nil break this code segment?

Posted: Sun Jan 24, 2010 12:37 pm
by krz
I'd like to amplify the previous comment.

In Common Lisp, nested if expressions can often be rewritten as a single cond, and it's generally considered good style to do so.

For example, you posted

Code: Select all

(defun our-member (obj lst)
  (if (null lst)
      nil
      (if (eql (car lst) obj)
          lst
          (our-member obj (cdr lst)))))
Which could be expressed more clearly (that is, less nesting) as the following. Generally, the deeper the nesting of your if statements, the clearer they get when you rewrite them into cond form.

Code: Select all

(defun our-member (obj lst)
  (cond
    ((null lst)
      nil)
    ((eql (car lst) obj)
      lst)
    (t
      (our-member obj (cdr lst)))))
Some folks would, given simple expressions like these, run the clauses together on the same line. It's bad for intricate expressions, but passable with simple stuff like this.

Code: Select all

(defun our-member (obj lst)
  (cond
    ((null lst) nil)
    ((eql (car lst) obj) lst)
    (t (our-member obj (cdr lst)))))

Re: Why does taking out nil break this code segment?

Posted: Sat Jan 30, 2010 4:45 am
by wentbackward
IF has a true part and false part. Taking out nil will mean the false part becomes the true part. But yes the first IF could be replaced by something simpler.

Code: Select all

(defun our-member (obj lst)
  (unless (null lst)
      (if (eql (car lst) obj)
          lst
          (our-member obj (cdr lst)))))
Would still return nil if lst was nil. The cond suggestion is probably better.