23
PRACTICAL COMMON LISP Peter Seibel http://www.gigamonkeys.com/book/ 1

PRACTICAL COMMON LISP

Embed Size (px)

DESCRIPTION

PRACTICAL COMMON LISP. Peter Seibel http://www.gigamonkeys.com/book/. CHAPTER 7 MACROS: STANDARD CONTROL CONSTRUCTS. MACROS: WHEN AND UNLESS. Macros can be used to extend the standard library of functionality in Common Lisp. - PowerPoint PPT Presentation

Citation preview

Page 1: PRACTICAL COMMON LISP

PRACTICAL COMMON LISP

Peter Seibelhttp://www.gigamonkeys.com/book/

1

Page 2: PRACTICAL COMMON LISP

CHAPTER 7 MACROS: STANDARD CONTROL CONSTRUCTS

2

Page 3: PRACTICAL COMMON LISP

MACROS: WHEN AND UNLESS Macros can be used to extend the standard library of functionality in

Common Lisp. This chapter introduces some standard control-construct macros

defined by Common Lisp. IF special operator provides the most basic form of conditional

execution—if x, do y; otherwise do z

(if condition then-form [else-form])

For example:(if (> 2 3) “Yes" "No") → "No"(if (> 2 3) “Yes") → NIL(if (> 3 2) “Yes" "No") → “Yes“ Here the then-form and else-form are each restricted to being a

single Lisp form.3

Page 4: PRACTICAL COMMON LISP

MACROS: WHEN AND UNLESS Another special operator, PROGN, executes any number of forms in order

and returns the value of the last form. For instance, suppose in the middle of a spam-filtering program you

wanted to both file a message as spam ( 垃圾郵件 ) and update the spam database when a message is spam.

(if (spam-p current-message) (progn (file-in-spam-folder current-message) (update-spam-database current-message)))

In this case, Common Lisp provides a standard macro WHEN: (when (spam-p current-message) (file-in-spam-folder current-message) (update-spam-database current-message))

4

Page 5: PRACTICAL COMMON LISP

MACROS: WHEN AND UNLESS If it wasn't built into the standard library, WHEN can be defined as:

(defmacro when (condition &rest body)

`(if ,condition (progn ,@body)))

A counterpart to the WHEN macro is UNLESS, which reverses the condition, evaluating its body forms only if the condition is false.

(defmacro unless (condition &rest body)

`(if (not ,condition) (progn ,@body)))

5

Page 6: PRACTICAL COMMON LISP

EXERCISES Define a recursive function build-list that will return a list of the first n

integers (either ascending or descending). It takes one integer n as an argument and returns a list of the first n integers.

> (build-list 5 '(a b c d e f g h))

(a b c d e)

Write an iterative version of build-list. Call it ibuild-list.

6

Page 7: PRACTICAL COMMON LISP

COND Another case of IF expression:

if a do x, else if b do y; else do z.(if a (do-x) (if b (do-y) (do-z)))

Common Lisp provides a macro for expressing multibranch conditionals: COND.(cond (test-1 form*) . . . (test-N form*)) 7

For example:(cond (a (do-x))

(b (do-y))

(t (do-z)))

Page 8: PRACTICAL COMMON LISP

AND, OR, AND NOT NOT is a function which takes a single argument and inverts its truth value,

returning T if the argument is NIL and NIL otherwise. AND and OR are macros and they implement logical conjunction and

disjunction of any number of subforms. AND stops and returns NIL as soon as one of its subforms evaluates to

NIL. If all the subforms evaluate to non-NIL, it returns the value of the last

subform. OR stops as soon as one of its subforms evaluates to non-NIL and

returns the resulting value. If none of the subforms evaluate to true, OR returns NIL. For examples:

(not nil) → T(not (= 1 1)) → NIL(and (= 1 2) (= 3 3)) → NIL(or (= 1 2) (= 3 3)) → T

8

Page 9: PRACTICAL COMMON LISP

LOOPING: DOLIST AND DOTIMES Lisp provides two macros, DOLIST and DOTIMES, for the common cases

of looping over the elements of a list and counting loops. DOLIST loops across the items of a list, executing the loop body with a

variable holding the successive items of the list.(dolist (var list-form [result-form]) body-form*)

For instance:CL-USER> (dolist (x '(1 2 3)) (print x))123NIL If you want to break out of a DOLIST loop before the end of the list,

you can use RETURN.CL-USER> (dolist (x '(1 2 3)) (print x) (if (evenp x) (return)))12NIL

9

Page 10: PRACTICAL COMMON LISP

LOOPING: DOLIST AND DOTIMES DOTIMES is the high-level looping construct for counting loops.

(dotimes (var count-form [result-form]) body-form*) The count-form must evaluate to an integer. Each time through the loop var holds successive integers from 0 to one

less than that number. For instance: CL-USER> (dotimes (i 4) (print i)) 0 1 2 3 NILBreak 4 [5]> (dotimes (i 4) (format t "~a" i) )0123NIL

10

Page 11: PRACTICAL COMMON LISP

LOOPING: DOLIST AND DOTIMES Because the body of both DOLIST and DOTIMES loops can contain

any kind of expressions, we can also nest loops.

For example, to print out the times tables from 1 × 1 = 1 to 20 × 20 = 400:

(dotimes (x 20)

(dotimes (y 20)

(format t "~3d " (* (1+ x) (1+ y))))

(format t "~%"))

11

Page 12: PRACTICAL COMMON LISP

EXERCISES What are the meanings of the following functions?

(defun it-intersection (x y) (let ((result-set nil)) (dolist (element x result-set) (when (member element y) (push element result-set)))))

(defun it-fact (n) (let ((prod 1)) (dotimes (i n prod) (setf prod (* prod (+ i 1))))))

12

Page 13: PRACTICAL COMMON LISP

EXERCISES What are the meanings of the following functions?

(defun rec-ffo (x) "Recursive function." (cond ((null x) nil) ((oddp (first x)) (first x)) (t (rec-ffo (rest x))))) (defun it-ffo (list-of-numbers) "Iterative function." (dolist (e list-of-numbers) (if (oddp e) (return e))))

13

Page 14: PRACTICAL COMMON LISP

DO DO loop lets us bind any number of variables and gives us complete

control over how they change on each step through the loop.

(do (variable-definition*)

(end-test-form result-form*)

statement*) Each variable-definition introduces a variable that will be in scope in

the body of the loop. The full form of a single variable definition is a list containing three

elements.

(var init-form step-form) The init-form will be evaluated at the beginning of the loop and the

resulting values bound to the variable var. Before each subsequent iteration of the loop, the step-form will be

evaluated and the new value assigned to var. The step-form is optional; if it's left out, the variable will keep its

value from iteration to iteration.14

Page 15: PRACTICAL COMMON LISP

DO(do (variable-definition*) (end-test-form result-form*) statement*)

At the beginning of each iteration, after all the loop variables have been given their new values, the end-test-form is evaluated.

As long as it evaluates to NIL, the iteration proceeds, evaluating the statements in order.

When the end-test-form evaluates to true, the result-forms are evaluated, and the value of the last result form is returned as the value of the DO expression.

15

Page 16: PRACTICAL COMMON LISP

DO For example:

(do ((n 0 (1+ n)) (cur 0 next) (next 1 (+ cur next))) ((= 10 n) cur))

The step forms (1+ n), next, and (+ cur next) are all evaluated using the old values of n, cur, and next.

Only after all the step forms have been evaluated are the variables given their new values.

Mathematically inclined readers may notice that this is a particularly efficient way of computing the eleventh Fibonacci number.

(do ((n 0 (1+ n)) (cur 0 next) (next 1 (+ cur next))) ((= 10 n) cur) (print cur))

0 1 1 2 3 5 8 13 21 34 5516

(do (variable-definition*) (end-test-form result-form*) statement*)

Note: > (1+ 2) 3

Page 17: PRACTICAL COMMON LISP

DO For example:

(do ((i 0 (1+ i))) ((>= i 4)) (print i))

> (do ((i 0 (1+ i))) ((>= i 4))(print i))0123NIL This loop is much more simply written using DOTIMES. (dotimes (i 4) (print i))

17

(do (variable-definition*) (end-test-form result-form*) statement*)

Page 18: PRACTICAL COMMON LISP

DO The next loop demonstrates a DO loop that binds no variables. It loops while the current time is less than the value of a global variable,

printing “Waiting” once a minute. Note that even with no loop variables, you still need the empty variables

list.(do () ((> (get-universal-time) *some-future-date*)) (format t "Waiting~%") (sleep 60))

A test example: (do () ((> 0 100)) (format t "Waiting~%") (sleep 6) ) PS: Ctrl-C to exit.

18

(do (variable-definition*) (end-test-form result-form*) statement*)

Page 19: PRACTICAL COMMON LISP

DO Another example:(defun read-a-number () (do ((answer nil)) (nil) (format t "~&Please type a number: ") (setf answer (read)) (if (numberp answer) (return answer)) (format t "~&Sorry, ~S is not a number. Try again.” answer)))

> (read-a-number)Please type a number: fooSorry, FOO is not a number. Try again.Please type a number: (1 2 3)Sorry, (1 2 3) is not a number. Try again.Please type a number: 3737

19

(do (variable-definition*) (end-test-form result-form*) statement*)

Page 20: PRACTICAL COMMON LISP

THE MIGHTY LOOP The LOOP macro actually comes in two flavors—simple and extended.

At least one of Common Lisp's original designers hated it.

The simple LOOP: An infinite loop doesn't bind any variables. (loop body-form*) The forms in body are evaluated each time through the loop, which will

iterate forever unless you use RETURN to break out. For example, you could write the previous DO loop with a simple LOOP.(loop (when (> (get-universal-time) *some-future-date*) (return)) (format t "Waiting ...~%") (sleep 1)) 20

Page 21: PRACTICAL COMMON LISP

THE MIGHTY LOOP The extended LOOP

It's distinguished by the use of certain loop keywords that implement a special-purpose language for expressing looping idioms.

For instance, here's an DO loop that collects the numbers from 1 to 10 into a list:(do ((nums nil) (i 1 (1+ i))) ((> i 10) (nreverse nums)) (push i nums)) → (1 2 3 4 5 6 7 8 9 10)

The LOOP version:

(loop for i from 1 to 10 collecting i) → (1 2 3 4 5 6 7 8 9 10)

21

Page 22: PRACTICAL COMMON LISP

THE MIGHTY LOOP The following are some more examples of simple uses of LOOP.

This sums the first ten squares:

(loop for x from 1 to 10 summing (expt x 2)) → 385 This counts the number of vowels in a string:

(loop for x across "the quick brown fox jumps over the lazy dog”

counting (find x "aeiou")) → 11 This computes the eleventh Fibonacci number, similar to the DO loop

used earlier:(loop for i below 10 and a = 0 then b and b = 1 then (+ b a) finally (return a))→ 55 The symbols across, and, below, collecting, counting, finally, for, from,

summing, then, and to are some of the loop keywords whose presence identifies these as instances of the extended LOOP.

22

Page 23: PRACTICAL COMMON LISP

TIME MACRO FUNCTION The TIME macro function tells you how long it took to evaluate an

expression. It may also tell you how much memory was used during the

evaluation, and other useful things. The exact details of what TIME measures and how the information is

displayed are implementation dependent. Here is an example:

(defun addup (n) "Adds up the first N integers" (do ((i 0 (+ i 1)) (sum 0 (+ sum i))) ((> i n) sum)))> (time (addup 1000000)) Real time: 0.9600013 sec.Run time: 0.9672062 sec.Space: 15668356 BytesGC: 26, GC time: 0.0468003 sec.500000500000 23