Conditionals

<-Previous | ^UP^ | Next->

We use conditionals mainly to implement logic in our computer programs. Common Lisp provides the following conditionals

  • if
  • when
  • unless
  • cond
  • case

if

if is a special operator.

The syntax is (if test then [else]), where

  • test is an expression that will evaluate to T or NIL
  • then is an expression that is evaluated and its value returned if test is T
  • else is an optional argument which is an expression to be evalued and its value returned if test is NIL.
  • if else is not provided, but test returns NIL, then if also returns NIL

GDL-USER> (if (> 1 0) (+ 3 4))

7

GDL-USER> (if (< 1 0) (+ 3 4))

NIL

GDL-USER> (if (< 1 0) (+ 3 4)(+ 5 6))

7

if only allows single expressions for the then and else arguments; if multiple expressions needs to be evaluated then the must be wrapped in a progn (which returns the last evaluated value in its body) or prog1 (which evaluates all expressions in its body in order and finally returns the value of the first). Not that it matters much to the routine user, but note that progn is a special operator, whilst prog1 is a macro (which you might imaging utilizes the former in its implementation).

GDL-USER> (defparameter *evaluated* NIL)

*EVALUATED*GDL-USER>  (if (> 1 0)(progn (+ 1 2)(setf *evaluated* (not *evaluated*))(+ 4 5))(+ 3 2))9

GDL-USER> *evaluated*

TGDL-USER>  (if (> 1 0)(prog1 (+ 1 2)(setf *evaluated* (not *evaluated*))(+ 4 5))(+ 3 2))3

GDL-USER> *evaluated*

NIL

when

when is a macro.

The syntax is (when test body), where

  • testis an expression that will evaluate to T or NIL.
  • body is one or more expressions, an implicit progn such that each expression will be evaluated in order, and finally the value of the last one returned.
  • if test returns NIL, then the when expression likewise returns NIL

GDL-USER> (defparameter *evaluated* NIL)

*EVALUATED*GDL-USER>  (when (> 1 0)(+ 1 2)(setf *evaluated* (not *evaluated*))(+ 4 5))9

GDL-USER> *evaluated*

TGDL-USER>  (when (< 1 0)(+ 1 2)(setf *evaluated* (not *evaluated*))(+ 4 5))

unless

unless is a macro and acts as the opposite of when. It takes the same arguments, but body is only evaluated when test returns NIL)

cond

cond is a macro which allows multiple conditions to be tested. The code associated with each condition is wrapped in an implicit progn allowing multiple expressions to be evaluated per condition. Each test and body of expressions is specified as a list and the body of expressions associated with the first test to evaluate to T is evaluated and the value of the last expression in that body returned. Once a test has evaluated to T, no further tests are evaluated.
GDL-USER>  (cond ((> 1 2) (+ 3 4))((string= "peter" "paul") (+ 5 6))((equalp (list 1 2) (list 1 2)) (setq a (+ 7 8))(oddp a))(T 0))T

In the example above, the final test is always T so this is effectively the default if none of the prior test expressions return T

case

case is a macro which takes an object as a first argument, followd by lists of key-expression pairs. The keyexpression pairs are then tested in order, and where the object is eql to the key, or a member of the key, then the corresponding expression is evaluated and returned. Optionally T or otherwise may be specified as a catchall key at the end of the case expression, meaning that its corresponsing expression will be evauated and returned if and only if object is not eql to, or a member of, any of the keys. Without such a otherwise or T, NIL would be returned in such a non-matching situation.

GDL-USER>  (defun month-days (month &key (leap-year nil))(case month((1 3 5 7 8 10 12) 31)((4 6 9 11) 30)(2 (if leap-year 29 28))(otherwise "month must be between 1 and 12"))) MONTH-DAYS

GDL-USER> (month-days 3)

31

GDL-USER> (month-days 2 :leap-year t)

29

GDL-USER> (month-days 13)

month must be between 1 and 12

We could extend assembly-8, used in the Lists tutorial as follows to make it a bit more flexible, ensuring that the number of box centers defined will always match the number of boxes to be created

(define-object assembly-8 (base-object):input-slots((box-lengths (list 2 5 8 12))):computed-slots((number-of-boxes (if (> (length (the box-lengths)) 3)3(length (the box-lengths))))(box-centers (case (the number-of-boxes)(1 (list (make-point 0 0 0)))(2 (list (make-point 0 0 0)(make-point 6 0 0)))(3 (list (make-point 0 0 0)(make-point 6 0 0)(make-point 12 0 0)))))(box-volumes (list-elements (the my-box) (the-element volume)))(box-1-volume (nth 0 (the box-volumes)))):objects((my-box :type 'box:sequence (:size (the number-of-boxes)):length (nth (the-child index) (the box-lengths)):width 2:height 1:center (nth (the-child index) (the box-centers)))))

GDL-USER> (setq self (make-object 'assembly-8))

#<ASSEMBLY-8 #x2103F266ED>

GDL-USER> (the box-centers)

(#(0.0 0.0 0.0) #(6.0 0.0 0.0) #(12.0 0.0 0.0))

GDL-USER> (setq self (make-object 'assembly-8 :box-lengths (list 3 8)))

#<ASSEMBLY-8 #x2103F25B2D>

GDL-USER> (the box-centers)

(#(0.0 0.0 0.0) #(6.0 0.0 0.0))

Resources

using-conditionals.lisp