Why does taking out nil break this code segment?

Discussion of Common Lisp
yougene
Posts: 23
Joined: Thu Jan 21, 2010 1:23 pm

Why does taking out nil break this code segment?

Post by yougene » Thu Jan 21, 2010 1:26 pm

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?

ramarren
Posts: 613
Joined: Sun Jun 29, 2008 4:02 am
Location: Warsaw, Poland
Contact:

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

Post by ramarren » Thu Jan 21, 2010 2:51 pm

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.

gugamilare
Posts: 406
Joined: Sat Mar 07, 2009 6:17 pm
Location: Brazil
Contact:

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

Post by gugamilare » Thu Jan 21, 2010 3:51 pm

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.

yougene
Posts: 23
Joined: Thu Jan 21, 2010 1:23 pm

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

Post by yougene » Fri Jan 22, 2010 1:01 pm

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?

yougene
Posts: 23
Joined: Thu Jan 21, 2010 1:23 pm

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

Post by yougene » Fri Jan 22, 2010 1:07 pm

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"?

ramarren
Posts: 613
Joined: Sun Jun 29, 2008 4:02 am
Location: Warsaw, Poland
Contact:

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

Post by ramarren » Fri Jan 22, 2010 1:48 pm

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.

hewih
Posts: 30
Joined: Tue Jan 19, 2010 9:36 am

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

Post by hewih » Sat Jan 23, 2010 4:15 pm

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")
)

ramarren
Posts: 613
Joined: Sun Jun 29, 2008 4:02 am
Location: Warsaw, Poland
Contact:

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

Post by ramarren » Sat Jan 23, 2010 4:46 pm

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.

krz
Posts: 2
Joined: Sun Jan 24, 2010 10:57 am
Location: Rochester, New York, USA
Contact:

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

Post by krz » Sun Jan 24, 2010 12:37 pm

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)))))

wentbackward
Posts: 2
Joined: Sat Jan 30, 2010 4:08 am

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

Post by wentbackward » Sat Jan 30, 2010 4:45 am

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.

Post Reply