• Aucun résultat trouvé

THE EVALUATOR

Dans le document COMMON LISP An Interactive Approach (Page 151-159)

I have said that a form is aLispobject intended to be evaluated and that a list form must begin with a function name. We have now had practice writing functions that build and return lists. If the list returned by such a function were a form, could we somehow evaluate it? What normally happens to such a returned list? Either it is printed by theLisplistener, or it is returned to some function that passes it as an argument to some other function where it is bound to a variable. If theLisplistener prints a form, we can just type it back as the next line. SinceLispevaluates and prints the value of every form we type in, this will causeLisp to evaluate a form it has just built. What if a variable inside a function is bound to a form? To evaluate it, we would need to pass it to some evaluator. Lispobviously has such an evaluator, and, marvelously enough, it lets us use it.

I say “marvelously enough” because there is great power in programmers’

having the ability to use the evaluator of the programming language they are using. Given that, and the ability to write programs that build source code of that programming language, we can write a program that “writes” and “runs”

its own programs!Lispprograms (functions) can obviously buildLispsource code (the source code just consists ofLisplists), so Lispprogrammers have this power. We can use it, if we want, to extend Lisp into a completely different programming language.

Lisp’s evaluator is the function eval, a function of one argument. It gets its single argument evaluated, and it evaluates it one more time and returns that value. For example:

> (eval 5)

127

128 II: PROGRAMMING IN PURE LISP

Normally, when we type a list to the Lisp listener or include one in a function definition, we have a choice of two extremes: if we quote the list, none of its members will be evaluated; if we don’t quote it, each of its members except the first will be evaluated and the list itself will be evaluated. These extremes are illustrated in the two interactions below:

> ’(cons pi (list "are" "squared")) (CONS PI (LIST "are" "squared"))

> (cons pi (list "are" "squared")) (3.1415926535897936d0 "are" "squared")

What if, however, we want some of the members of the list to be evaluated and other members not to be? One way of doing this is to use list to construct the list, quote the members we don’t want evaluated, and not quote the members we do want evaluated. Another way is to quote all members, but wrap a call to evalaround those members we want evaluated. So, for example:

> (list ’cons pi ’(list "are" "squared"))

(CONS 3.1415926535897936d0 (LIST "are" "squared"))

> (list ’cons (eval ’pi) ’(list "are" "squared")) (CONS 3.1415926535897936d0 (LIST "are" "squared"))

A more convenient way of doing this is to use the Common Lisp backquote character. This is the single quote character on your keyboard other than the one you have been using for the quote character. In this text, the backquote character will look like‘, whereas the regular quote character looks like ’. The backquote works just like the quote, except that if you backquote a tree, any subtree, no matter how deep, that is preceded by a comma is evaluated. For example:

> ‘(cons ,pi (list "are" "squared"))

(CONS 3.1415926535897936d0 (LIST "are" "squared"))

> ‘(cons pi ,(list "are" "squared")) (CONS PI ("are" "squared"))

> ‘("me" ("and" ,(list "my" "shadow"))) ("me" ("and" ("my" "shadow")))

19: The Evaluator 129 If the comma is followed by@, then what comes after must evaluate to a list, and it is “spliced” into the outer list. For example:

> ‘(cons pi ,@(list "are" "squared")) (CONS PI "are" "squared")

> ‘("me" ("and" ,@(list "my" "shadow"))) ("me" ("and" "my" "shadow"))

We will be using the backquote in a function definition below.

To get an idea of how to use eval, we will look at how the equivalent of infinite lists can be constructed using the technique known aslazy evaluation.

For example, say we want to have a list of all the natural numbers. Since that is an infinite list, and we can’t really construct it, instead we will construct a list containing a few natural numbers and a “promise” to construct the rest.

A promise will look like a list whosefirstmember is the keyword:promise (see page 102 if you’ve forgotten about keywords) and whoserestis a form that will evaluate to some more of the list, again ending with a promise. For example, the function natural-numbers-fromwill return an infinite list of natural numbers, consisting of just one natural number and a promise of the rest:

(defun natural-numbers-from (n)

"Returns an infinite list of the natural numbers starting from N using the lazy evaluation technique."

(check-type n integer)

‘(,n :promise natural-numbers-from ,(1+ n))) Some lists generated by this function are

> (natural-numbers-from 0)

(0 :PROMISE NATURAL-NUMBERS-FROM 1)

> (natural-numbers-from 5)

(5 :PROMISE NATURAL-NUMBERS-FROM 6)

To use these lists, we need to write the functionslazy-firstandlazy-rest.

These functions will act likefirstandrestunless they bump into the key-word:promise, in which case they will redeem the promise once before doing their usual thing:

(defun lazy-first (list)

"Returns the first member of LIST, redeeming a promise if necessary."

(check-type list list)

(if (eql (first list) :promise) (first (eval (rest list))) (first list)))

130 II: PROGRAMMING IN PURE LISP

(defun lazy-rest (list)

"Returns the rest of the LIST, redeeming a promise if necessary."

(check-type list list)

(if (eql (first list) :promise) (rest (eval (rest list))) (rest list)))

A few examples may clarify this:

> (lazy-first (natural-numbers-from 0)) 0

> (lazy-rest (lazy-rest (natural-numbers-from 0))) (:PROMISE NATURAL-NUMBERS-FROM 2)

> (lazy-first

(lazy-rest (lazy-rest (natural-numbers-from 0)))) 2

The name “lazy evaluation” comes from the fact that only those members of the list that are actually accessed are computed.

For a final example of usingeval, consider the functionprefixyou have stored in the file named calculator. The value of

(prefix ’(7 + 12 / 4 - 2 * 3))

is(- (+ 7 (/ 12 4)) (* 2 3)). We could easily define the functioncompute to compute arithmetic expressions written in normal syntax as follows:

(defun compute (expr)

"Returns the value of the arithmetic expression EXPR.

EXPR is to be a list containing an expression in normal, infix notation."

(check-type expr list) (eval (prefix expr)))

The value of (compute ’(7 + 12 / 4 - 2 * 3))should then be4.

Exercises

Do the exercises of this chapter in the packagech19except where otherwise instructed.

19.1 (d) Evaluate (cons ’first ’(’(a))). Then type the value back to theLisplistener exactly as it printed it to you.

19: The Evaluator 131 19.2 (r)Have LISP evaluate

a. (eval 5) b. (eval a) c. (eval ’a) d. (eval ’’a)

e. (eval (first (a))) f. (eval (first ’(a))) g. (eval ’(first (a))) h. (eval ’(first ’(a)))

i. (eval (cons ’first ’((a)))) j. (eval (cons ’first ’(’(a))))

19.3 (r)Try for yourself all the examples of using backquote shown in this chapter.

19.4 (r) Define natural-numbers-from as shown in this chapter.

Check the values of

(natural-numbers-from 0) and

(natural-numbers-from 5).

19.5 (r)Definelazy-firstandlazy-restas shown in this chapter. Check the values of

(lazy-first (natural-numbers-from 0))

(lazy-rest (lazy-rest (natural-numbers-from 0))) and

(lazy-first

(lazy-rest (lazy-rest (natural-numbers-from 0))))

19.6 (i) Define (lazy-nth n list) to return the nth member of the list, redeeming promises where necessary. Compare this with nth (Exer-cise 16.12). Check the value of

(lazy-nth 15 (natural-numbers-from 0)) To check thatnis a positive integer, you can use (check-type n (and integer (satisfies plusp)))

132 II: PROGRAMMING IN PURE LISP 19.7 (d)The Fibonacci numbers are the sequence 0,1,1,2,3,5,8, . . .,where each number after the first two is the sum of the previous two numbers.

A general Fibonacci sequence may be generated from any two numbersn andmand is the sequencen, m, n+m, n+2m,2n+3m, . . .,where again each number after the first is the sum of the previous two. Define the function(fibonacci-from n m)to return the infinite list of Fibonacci numbers generated fromnandmusing the technique of lazy evaluation.

Usinglazy-nth, check that the seventh Fibonacci number is 8 and that the twentieth is 4,181.

19.8 (d) Define the function(relatively-prime n integers)where n is a positive integer andintegersis a list of positive integers, to return False if (= (mod n i) 0) for any integer i in others and True otherwise. ( (mod n m)is the remainder after dividingnbym.)

19.9 (d)Usingrelatively-prime, define the function(primes-from n oth-ers), where nis a positive integer andothersis a list of all primes less thann, to return an infinite list of prime numbers equal to or greater thann,using the technique of lazy evaluation. (Hint: If(relatively-prime n others), thennis the first prime on the desired list; otherwise the list is generated by(primes-from (1+ n) others).)

19.10 (p2) Define computeas in this chapter, and store it on the file named calculator. Check that

(compute ’(7 + 12 / 4 - 2 * 3)) evaluates to 4.

19.11 (p2)Modify the functions in yourcalculatorfile so that (prefix ’(((5)))) = 5

(prefix ’((5 + 3) / 4)) = (/ (+ 5 3) 4) (prefix ’(4 * (10 - 7))) = (* 4 (- 10 7))

(prefix ’(2 ^ (5 + 4) * 3)) = (* (^ 2 (+ 5 4)) 3) 19.12 (p2)Modify yourprefixfunction so that

(compute ’(2 ^ 10 - 24)) evaluates to 1000.

19.13 (p2) Modify the functions in your calculator file to handle unary+ and unary-.

19: The Evaluator 133 19.14 (p2)TheCommon Lispfunction(fboundp s)returns True if the sym-bol s is the name of a defined function. Modify the functions in your calculatorfile so that if the first member of the list given toprefixis the name of aLispfunction or if the first member of any sublist of that list is the name of aLispfunction, that list or sublist is not modified.

19.15 (d)Redo Exercise 4.17 usingcompute.

19.16 (p2) Redo Exercise 11.4 using compute. Store discrim and quad-rootsin the filecalculator.

CHAPTER 20

Dans le document COMMON LISP An Interactive Approach (Page 151-159)