![]() |
![]() | |||||
|
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]()
|
Flow Control
Conditional ExecutionThe 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. 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) 5Here 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) NILSimilarly, 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) 3when 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 2If the action in the selected cond clause is missing, cond returns what the condition evaluated to: >(cond ((+ 3 4))) 7Here 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)) 3The 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 objectsA Common Lisp compiler may choose to issue a warning if a clause cannot be selected because it is completely shadowed by earlier clauses.
IterationCommon Lisp provides a number of iteration constructs.
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))) NILHere 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 NILA 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
#
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 OutputReading 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
| |||||
| |
| Added to the Web: November 16, 1999. Last updated: November 16, 1999. Web page design by Dan Solarek. |
![]() http://cset.sp.utoledo.edu/cset4250/ |