Please send all questions & assignments to:
dsolarek@eng.utoledo.edu

 

LISP
Flow Control

Conditional Execution

The traditional conditional construct in LISP is cond. However, the if construct is much simpler and is directly comparable to conditional constructs in other programming languages. Along with the if special form, this page describes several other LISP conditional and related constructs, including:
  • if
  • progn
  • when and unless
  • cond
  • case and otherwise
  • typecase
The last two, case and typecase, are considered dispatching constructs in LISP. They are often more convenient and efficient than cond.

if. The simplest construct for conditional execution is the if special form. The if is considered to be primitive in Common LISP. The syntax for the if special form is illustrated below:

 (if test then else) 
The first argument of if determines whether the second (then) or third (else) argument will be executed:
 >(if t 5 6)
 5
 >(if nil 5 6)
 6
 >(if 4 5 6)
 5
Here are some other examples.
 >(setf a 7)
 7

 >(setf b 2)
 2

 >(if (< a b) a b)
 2

 >(if (> a b) a b)
 7

 >
These two are equivalent to:
 >(max a b)
 7

 >(min a b)
 2

 >
Here is one more:
 >(setf x -2)
 -2

 >(if (minusp x) (- x) x)
 2

 >(abs x)
 2

 >(setf x 9)
 9

 >(if (minusp x) (- x) x)
 9

 >(abs x)
 9

 >
progn. If you need to put more than one statement in the then or else clause of an if statement, you can use the progn special form. Progn executes each statement in its body, then returns the value of the final one.
 >(setq a 7)
 7
 >(setq b 0)
 0
 >(setq c 5)
 5
 >(if (> a 5)
    (progn
      (setq a (+ b 7))
      (setq b (+ c 8)))
    (setq b 4))
 13
when and unless. An if statement which lacks either a then or an else clause can be written using the when or unless special forms. The syntax for the when special form is:
 (when test form1 form2 ... )
An example of this form is shown below:
 >(when t 3)
 3
 >(when nil 3)
 NIL
Similarly, the syntax for the unless form is:
 (unless test form1 form2 ... )
which can be implemented as shown below:
 >(unless t 3)
 NIL
 >(unless nil 3)
 3
when and unless, unlike if, allow any number of statements in their bodies, e.g., (when x a b c) is equivalent to (if x (progn a b c)).
 >(when t
    (setq a 5)
    (+ a 6))
 11
cond. More complicated conditionals can be defined using the cond special form, which is equivalent to an if ... else if ... endif construction. A cond consists of the symbol cond followed by a number of cond clauses, each of which is a list. The first element of a cond clause is the condition; the remaining elements (if any) are the action. The cond form finds the first clause whose condition evaluates to true (i.e., does not evaluate to nil); it then executes the corresponding action and returns the resulting value. None of the remaining conditions are evaluated; nor are any actions except the one corresponding to the selected condition. For example:
 >(setq a 3)
 3
 >(cond
   ((evenp a) a)      ;if a is even return a
   ((> a 7) (/ a 2))  ;else if a is bigger than 7 return a/2
   ((< a 5) (- a 1))  ;else if a is smaller than 5 return a-1
   (t 17))            ;else return 17
 2
If the action in the selected cond clause is missing, cond returns what the condition evaluated to:
 >(cond ((+ 3 4)))
 7
Here is a recursive function which uses cond.
 >(defun testfcn (x steps)
    (cond
     ((= x 1) steps)
     ((oddp x) (testfcn (+ 1 (* x 3)) (+ 1 steps)))
     (t (testfcn (/ x 2) (+ 1 steps)))))
 A
 >(testfcn 7 0)
 16
case. The case form is a conditional that chooses one of its clauses to execute by comparing a value to various constants, which are typically keyword symbols, integers, or characters (but may be any objects). Its syntax is as follows:
 (case keyform 
   (keylist-1 consequent-1-1 consequent-1-2 ...) 
   (keylist-2 consequent-2-1 ...) 
   (keylist-3 consequent-3-1 ...) 
   ...)
Structurally case is much like cond, and it behaves like cond in selecting one clause and then executing all consequents of that clause. However, case differs in the mechanism of clause selection. As the example below shows, the LISP case statement is like a C switch statement:
 >(setq x 'b)
 B
 >(case x
   (a 5)
   ((d e) 7)
   ((b f) 3)
   (otherwise 9))
 3
The otherwise clause at the end means that if x is not a, b, d, e, or f, the case statement will return 9.

typecase. The typecase form is a conditional that chooses one of its clauses by examining the type of an object. Its form is as follows:

 (typecase keyform 
   (type-1 consequent-1-1 consequent-1-2 ...) 
   (type-2 consequent-2-1 ...) 
   (type-3 consequent-3-1 ...) 
   ...)
Structurally typecase is much like cond or case, and it behaves like them in selecting one clause and then executing all consequents of that clause. It differs in the mechanism of clause selection.

It is permissible for more than one clause to specify a given type, particularly if one is a subtype of another; the earliest applicable clause is chosen. Thus for typecase, unlike case, the order of the clauses may affect the behavior of the construct. For example:

 (typecase an-object 
   (string ...)            ;This clause handles strings 
   ((array t) ...)         ;This clause handles general arrays 
   ((array bit) ...)       ;This clause handles bit arrays 
   (array ...)             ;This handles all other arrays 
   ((or list number) ...)  ;This handles lists and numbers 
   (t ...))                ;This handles all other objects
A Common Lisp compiler may choose to issue a warning if a clause cannot be selected because it is completely shadowed by earlier clauses.

 

Iteration

Common Lisp provides a number of iteration constructs.
  • The loop construct provides a trivial iteration facility; it is little more than a progn with a branch from the bottom back to the top.
  • The do and do* constructs provide a general iteration facility for controlling the variation of several variables on each cycle.
  • For specialized iterations over the elements of a list or n consecutive integers, dolist and dotimes are provided.
  • The tagbody construct is the most general, permitting arbitrary go statements within it. (The traditional prog construct is a synthesis of tagbody, block, and let.)
Most of the iteration constructs permit statically defined non-local exits (see return-from and return).

loop. The simplest iteration construct in LISP is loop. The loop construct implements an indefinite loop, repeatedly executing its body until it encounters a return special form. The basic syntax for the loop construct is:

 (loop {form}*)
This construct executes form(s) repeatedly until exited by a throw or return. The form(s) are surrounded by an implicit NIL block. For example,
 >(setq a 4)
 4
 >(loop 
   (setq a (+ a 1))
   (when (> a 7) (return a)))
 8
 >(loop
  (setq a (- a 1))
  (when (< a 3) (return)))
 NIL
Here is another example:
 >(let ((n 0))
    (loop
      (when (> n 10) (return))
      (print n) (prin1 (* n n))
      (incf n)))

 0 0
 1 1
 2 4
 3 9
 4 16
 5 25
 6 36
 7 49
 8 64
 9 81
 10 100
 NIL

 >
do. The do special form provides a generalized iteration capability, with an arbitrary number of index variables. These variables are bound within the iteration and stepped in parallel in specified ways. They may be used both to generate successive values (such as successive integers) or to accumulate results. When an end condition is met, the iteration terminates with a specified value. To understand this better, let's look at the general syntax of do:
 (do ((var1 init1 step1)
      (var2 init2 step2)
      ...)
     (end-test result)
   statement1
   ...)

An example do special form is illustrated below:

 >(do ((x 1 (+ x 1))
       (y 1 (* y 2)))
      ((> x 5) y)
    (print y)
    (print 'working))
 1 
 WORKING 
 2 
 WORKING 
 4 
 WORKING 
 8 
 WORKING 
 16 
 WORKING 
 32 
The first part of a do specifies what variables to bind, what their initial values are, and how to update them. The second part specifies a termination condition and a return value. The last part is the body. A do form binds its variables to their initial values like a let, then checks the termination condition. As long as the condition is false, it executes the body repeatedly; when the condition becomes true, it returns the value of the return-value form.

Another example:

 >(defun my-exp (m n)      ; raise m to power of n
     (do ((result 1)
          (exp    n))
         ((= exp 0) result)
         (setq result (* result m))
         (setq exp    (- exp 1))))
 MY-EXP

 >(my-exp 5 3)
 125

 >
do*. The do* form is to do as let* is to let. Syntax:
 (do* ({(var [init [step]])}*) (endtest {result}*)
          {decl}* {tag | statement}*)
Just like do, but performs variable bindings and assignments in serial, just like let* and setq do.

dolist. The dolist structure binds a variable to the elements of a list in order and stops when it encounters the end of the list. Syntax:

 (dolist (var listform [result]) {decl}* {tag | statement}*)
Executes statement(s), with var bound to each member of the list value of listform. Then returns the value(s) of result (which defaults to NIL). For example,
 >(dolist (x '(a b c)) (print x))
 A 
 B 
 C 
 NIL 
A Dolist always returns nil. Note that the value of x in the above example was never nil. The NIL that appears below the letter C is the value that the dolist function returned, printed in the normal course of the read-eval-print loop.

Here is another example.

 >(dolist (item `(1 foo "Hello" 79.3 2/3 ,#'abs))
    (format t "~&~S is a ~A~%" item (type-of item)))
 1 is a FIXNUM
 FOO is a SYMBOL
 "Hello" is a STRING
 79.299999999999997 is a LONG-FLOAT
 2/3 is a RATIO
 # is a COMPILED-FUNCTION
 NIL

 >
dotimes. The dotimes is a looping construct that evaluates a list of expressions a given sumber of times. The body of dotimes may contain any number of expressions. The syntax for the dotimes form is:
 (dotimes (var countform [result]) {decl}* {tag | statement}*)
The dotimes construct executes statement(s), with var bound to each number between 0 (inclusive) and the value of countform (exclusive). Then returns the value(s) of result (which defaults to NIL). A simple example of the dotimes form is:
 >(dotimes (n 100 "all done") (print n))
As shown below, this dotimes form will print 0, 1, 2, ..., 99, and return the string "all done" as the value of the expression.
 0
 1
 2
 3
 4
 .
 .
 .
 96
 97
 98
 99
 "all done"

 >
Here is another example:
 >(defun power-i (x y)
            (let ((result 1))
               (dotimes (count y result)
                  (setf result (* x result)))))
 POWER-I

 >(power-i 2 4)
 16

 >
And another:
 >(dotimes (n 11)
    (print n) (prin1 (* n n)))

 0 0
 1 1
 2 4
 3 9
 4 16
 5 25
 6 36
 7 49
 8 64
 9 81
 10 100
 NIL

 >
tagbody. Syntax:
 (tagbody {tag | statement}*)
The tagbody construct executes statement(s) and returns NIL if it falls off the end.

The example below uses prog and go.

 >(defun iter ()
   (prog ((y 1))
     again
       (setq y (+ 1 y))
       (print y)
       (if (not(= 6 y)) (go again))))
 ITER

 >(iter)

 2
 3
 4
 5
 6
 NIL

 >
Another example:
 >(defun iter (x)
   (prog ((y 1))
    again
     (setq y (+ 1 y))
      (if(= x y) (RETURN "Y IS EQUAL TO X")
       (print y))
        (go again)))
 ITER

 >(iter 6)

 2
 3
 4
 5
 "Y IS EQUAL TO X"

 >

Input and Output

Reading from a data file:
 >(with-open-file (myinput-stream "data" :direction :input)
   (loop
     (if (eq (print (read myinput-stream nil)) nil)
      (return '---EndOfFile---))))

 1
 2
 3
 4
 NIL
 ---ENDOFFILE---

 >
Eliminating the NIL at the end of the read:
 >(with-open-file (myinput-stream "data" :direction :input)
   (loop
     (setq myline (read myinput-stream nil))
     (if (eq myline nil)
      (return '---EndOfFile---)
      (print myline))))

 1
 2
 3
 4
 ---ENDOFFILE---

 >
The examples above read the file called data which has the following content:
 1
 2
 3
 4
    Click on the button at left to return to the calling page.

 

There have been visitors since 11/26/2003

Added to the Web: November 16, 1999.
Last updated: November 16, 1999.
Web page design by Dan Solarek.

http://cset.sp.utoledo.edu/cset4250/