the case primitive: matches the evaluated key form against the unevaluated keys by using eql the...

27
The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case <key form> (<key 1> <consequent 1-1> ... <consequent 1- n>) (<key 2> <consequent 2-1> ... <consequent 2- n>) ..... (<key m> <consequent m-1> ... <consequent m- n>)) Example: * (setf object 'sphere r 2) 2 * (case object (circle (* pi r r)) (sphere (* 4 pi r r))) 50.2654824574367

Upload: crystal-heather-gibbs

Post on 29-Dec-2015

221 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

The Case primitive: matches the evaluated key form against the unevaluated keys by using eql

The general format of case is the following:

(case <key form>

(<key 1> <consequent 1-1> ... <consequent 1-n>)

(<key 2> <consequent 2-1> ... <consequent 2-n>)

.....

(<key m> <consequent m-1> ... <consequent m-n>))

Example:* (setf object 'sphere r 2)

2

* (case object

(circle (* pi r r))

(sphere (* 4 pi r r)))

50.2654824574367

Page 2: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

The key may be an atom or a list; in the latter case the list is searched for the key by means of the member predicate

Example:

* (case object

((circle wheel) (* pi r r))

((ball sphere) (* 4 pi r r))

(otherwise 0)) ; if none of the clauses is triggered, case

50.2654824574367 ; returns NIL, the otherwise clause (or t)

; can be used to set the returned value.

Page 3: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

The problem reduction technique

Divide the problem into sub-problems each of which can be

handled separately.

Example: the both-ends procedure (defun both-ends (whole-list)

(case (length whole-list)

(0 ....) ==> (0 nil)

(1 ....) ==> (1 (cons (first whole-list) whole-list))

(2 ....) ==> (2 whole-list)

(t ....))) ==> (t (cons (first whole-list)

(last whole-list)))))

Page 4: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

The procedure abstraction technique: arrange procedures into “abstraction” layers.

Example: the both-ends procedure* (defun both-ends (whole-list) (make-list (get-first-el whole-list) (get-last-el whole-list)))BOTH-ENDS* (defun make-list (el-1 el-2) (list el-1 el-2))MAKE-LIST* (defun get-first-el (list-1) (first list-1))GET-FIRST-EL* (defun get-last-el (list-1) (first (last list-1)))GET-LAST-EL

Page 5: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

Defvar and defparameter define global variables

(defvar *global*) declares *global* as a global variable.

(defvar *global* expression) assigns a value to *global*

(defparameter *global* expression) declares *global* as a global variable, and assings a value to it.

Examples:* (defvar *var-1*) *

(defparameter *par-1*)

*VAR-1* ERROR

* (defvar *var-1* 222) * (defparameter *par-1* 77)

*VAR-1* 77

* *var-1* * *par-1*

222 77

* (defvar *var-1* 333) * (defparameter *par-1* 88)

*VAR-1* *PAR-1*

* *var-1* * *par-1*

222 88

Page 6: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

Singly recursive procedures: each recursive call originates only one new call

Example: compute the “n” power of m.* (defun exponent (m n)

(if (zerop n)

1

(* m (exponent m (- n 1)))))

EXPONENT

* (exponent 2 3)

8

The behavior of recursive procedures can be observed by means of the

trace primitive. Untrace will undo the effect of trace. These have the

following syntax:(trace <procedure name>)

(untrace <procedure name>)

Page 7: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

* (trace exponent)

(EXPONENT)

* (exponent 2 3)

| 1 Entering: EXPONENT, argument-list: (2 3)

| 2 Entering: EXPONENT, argument-list: (2 2)

| | 3 Entering: EXPONENT, argument-list: (2 1)

| | 4 Entering: EXPONENT, argument-list: (2 0)

| | 4 Exiting: EXPONENT, value 1

| | 3 Exiting: EXPONENT, value 2

| 2 Exiting: EXPONENT, value 4

| 1 Exiting: EXPONENT, value 8

8

Page 8: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

Doubly recursive procedures: each call originates two recursive calls

Example: compute Fibonacci numbers

* (defun fibonacci (n)

(if (or (= n 0) (= n 1))

1

(+ (fibonacci (- n 1))

(fibonacci (- n 2)))))

FIBONACCI

* (fibonacci 5)

8 * (trace fibonacci)

(FIBONACCI)

Page 9: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

* (fibonacci 5)

| 1 Entering: FIBONACCI, argument-list: (5)

| 2 Entering: FIBONACCI, argument-list: (4)

| | 3 Entering: FIBONACCI, argument-list: (3)

| | 4 Entering: FIBONACCI, argument-list: (2)

| | | 5 Entering: FIBONACCI, argument-list: (1)

| | | 5 Exiting: FIBONACCI, value 1

| | | 5 Entering: FIBONACCI, argument-list: (0)

| | | 5 Exiting: FIBONACCI, value 1

| | 4 Exiting: FIBONACCI, value 2

| | 4 Entering: FIBONACCI, argument-list: (1)

| | 4 Exiting: FIBONACCI, value 1

| | 3 Exiting: FIBONACCI, value 3

| | 3 Entering: FIBONACCI, argument-list: (2)

| | 4 Entering: FIBONACCI, argument-list: (1)

| | 4 Exiting: FIBONACCI, value 1

| | 4 Entering: FIBONACCI, argument-list: (0)

| | 4 Exiting: FIBONACCI, value 1

| | 3 Exiting: FIBONACCI, value 2

| 2 Exiting: FIBONACCI, value 5

| 2 Entering: FIBONACCI, argument-list: (3)

| | 3 Entering: FIBONACCI, argument-list: (2)

| | 4 Entering: FIBONACCI, argument-list: (1)

| | 4 Exiting: FIBONACCI, value 1

| | 4 Entering: FIBONACCI, argument-list: (0)

| | 4 Exiting: FIBONACCI, value 1

| | 3 Exiting: FIBONACCI, value 2

| | 3 Entering: FIBONACCI, argument-list: (1)

| | 3 Exiting: FIBONACCI, value 1

| 2 Exiting: FIBONACCI, value 3

| 1 Exiting: FIBONACCI, value 8

8

Page 10: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

Tail recursive procedures: a way to make recursion efficient

Example: compute the number of elements in a list (version 1)

* (defun count-elements-1 (list-1)

(if (endp list-1)

0

(+ 1 (count-elements-1 (rest list-1)))))

COUNT-ELEMENTS-1

* (setf list-1 '(a b c d))

* (count-elements-1 list-1)

| 1 Entering: COUNT-ELEMENTS-1, argument-list: ((A B C D))

| 2 Entering: COUNT-ELEMENTS-1, argument-list: ((B C D))

| | 3 Entering: COUNT-ELEMENTS-1, argument-list: ((C D))

| | 4 Entering: COUNT-ELEMENTS-1, argument-list: ((D))

| | | 5 Entering: COUNT-ELEMENTS-1, argument-list: (NIL)

| | | 5 Exiting: COUNT-ELEMENTS-1, value 0

| | 4 Exiting: COUNT-ELEMENTS-1, value 1

| | 3 Exiting: COUNT-ELEMENTS-1, value 2

| 2 Exiting: COUNT-ELEMENTS-1, value 3

| 1 Exiting: COUNT-ELEMENTS-1, value 4

4

Page 11: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

Tail recursion (cont.)

Example: compute the number of elements in a list (version 2)

* (defun count-elements-2 (list-1)

(count-elements-2-aux list-1 0))

COUNT-ELEMENTS-2

* (defun count-elements-2-aux (list-1 result)

(if (endp list-1)

result

(count-elements-2-aux (rest list-1) (+ 1 result))))

COUNT-ELEMENTS-2-AUX

* (count-elements-2 list-1)

| 1 Entering: COUNT-ELEMENTS-2-AUX, argument-list: ((A B C D) 0)

| 2 Entering: COUNT-ELEMENTS-2-AUX, argument-list: ((B C D) 1)

| | 3 Entering: COUNT-ELEMENTS-2-AUX, argument-list: ((C D) 2)

| | 4 Entering: COUNT-ELEMENTS-2-AUX, argument-list: ((D) 3)

| | | 5 Entering: COUNT-ELEMENTS-2-AUX, argument-list: (NIL 4)

| | | 5 Exiting: COUNT-ELEMENTS-2-AUX, value 4

| | 4 Exiting: COUNT-ELEMENTS-2-AUX, value 4

| | 3 Exiting: COUNT-ELEMENTS-2-AUX, value 4

| 2 Exiting: COUNT-ELEMENTS-2-AUX, value 4

| 1 Exiting: COUNT-ELEMENTS-2-AUX, value 4

4

Page 12: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

The Tower of Hanoi example

Version 1: uses a doubly recursive procedure.* (defun tower-of-hanoi-1 (list-of-disks)

(if (endp list-of-disks)

0

(+ (tower-of-hanoi-1 (rest list-of-disks))

1

(tower-of-hanoi-1 (rest list-of-disks)))))

TOWER-OF-HANOI-1

Version 2: uses a singly recursive procedure.* (defun tower-of-hanoi-2 (list-of-disks)

(if (endp list-of-disks)

0

(+ 1 (* 2 (tower-of-hanoi-2 (rest list-of-disks))))))

TOWER-OF-HANOI-2

Page 13: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

Iteration control structure: the DOTIMES primitive

Dotimes supports iteration on numbers. It has the following format:

(dotimes (<count parameter> <upper-bound form> <result form>)

<body>)

When dotimes is entered, the upper-bound form is evaluated resulting

in a number, say n. Numbers between 0 and n-1 are assigned to the

count parameter one by one, and for each one of these numbers the

body is evaluated. Finally, the result form is evaluated, and its value is

returned by dotimes (if the result form is missing, dotimes returns NIL).

Page 14: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

Example: compute the “n” power of m.

* (defun exponent-2 (m n)

(let ((result 1))

(dotimes (count n result)

(setf result (* m result)) )))

(print result)EXPONENT-2

* (trace exponent-2)

(EXPONENT-2)

* (exponent-2 3 3)

| 1 Entering: EXPONENT-2, argument-list: (3 3) ; trace is not very useful here.

| 1 Exiting: EXPONENT-2, value 27 ; Incorporating print forms to see

27 ; intermediate results is better .

Page 15: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

Dolist supports iteration on lists. Its format is the following:

(dolist (<element parameter> <list form> <result form>)

<body>)

When dolist is entered, the list form is evaluated returning a list of elements.

Each one of them is assigned to the element parameter, and the body is

evaluated. Finally, the result form is evaluated, and its value is returned by

dolist (if the result form is missing, dolist returns NIL).

Iteration control stucture: the DOLIST primitive

Page 16: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

Example: count number of a’s in a list.

* (defun count-a (list-1)

(let ((result 0))

(dolist (element list-1 result)

(when (eql element 'a)

(setf result (+ 1 result))))))

COUNT-A

* (setf list-1 '(a s d f a s d w a a))

(A S D F A S D W A A)

* (count-a list-1)

4

Page 17: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

DOTIMES and DOLIST can be terminated by the (return <expression>) form

Whenever (return <expression>) is encountered, dotimes/dolist

terminates and returns the value of <expression>.

Example: does the list contain at least “n” a’s? If yes, return them as soon as they are found.

* (defun find-n-a (n list-1)

(let ((result 0) (a-list nil))

(dolist (element list-1 a-list)

(cond ((<= n result) (return a-list))

((eql element 'a) (setf result (+ 1 result))

(setf a-list (cons element a-list)))))))

FIND-N-A

* (find-n-a 2 list-1)

(A A)

Page 18: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

The DO primitive works on both numbers and lists, and is more general than DOTIMES and DOLIST

Do has the following format:

(do ((<parameter 1> <initial value 1> <update form 1>)

(<parameter 2> <initial value 2> <update form 2>)

…..

(<parameter n> <initial value n> <update form n>))

(<termination test> <intermediate forms> <result form>)

<body>)

When do is entered, its parameters are set to their initial values in parallel

(in the same way as in the let form). If there are no parameters, an empty

parameter list must be provided. The termination test is evaluated always

before the body is evaluated. If it succeeds, intermediate forms and the

result form are evaluated; the value of the result form is returned by do (if

no result form is given, do returns NIL).

Page 19: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

Example: compute “n” power of m

Version 1:

* (defun exponent-3 (m n)

(do ((result 1)

(exponent n))

((zerop exponent) result)

(setf result (* m result))

(setf exponent (- exponent 1))))

EXPONENT-3

* (exponent-3 3 3)

27

Version 2:

* (defun exponent-4 (m n)

(do ((result 1)

(exponent n))

( )

(when (zerop exponent) (return result))

(setf result (* m result))

(setf exponent (- exponent 1))))

EXPONENT-4

* (exponent-4 3 3)

27

Page 20: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

DO* assigns initial values to parameters sequentially (like let*)

Example which does not work

* (defun exponent-5 (m n)

(do ((result m (* m result))

(exponent n (- exponent 1))

(counter (- exponent 1) (- exponent 1)))

((zerop counter) result)))

EXPONENT-5

* (exponent-5 3 3)

*** Debugger warning: leftover specials ***

>>> Error: {Determining function in error.}

>>> Error:Unbound variable: EXPONENT

(#<COMPILED-FUNCTION 3:E196> ...)

DO* solves the problem.* (defun exponent-6 (m n)

(do* ((result m (* m result))

(exponent n (- exponent 1))

(counter (- exponent 1) (- exponent 1)))

((zerop counter) result)))

EXPONENT-6

A better solution:* (defun exponent-7 (m n)

(do* ((result 1 (* m result))

(exponent n (- exponent 1))

(counter exponent (- counter 1)))

((zerop counter) result)))

EXPONENT-7

Page 21: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

The LOOP primitive implements infinite loop if (return <expression>) is not encountered

Loop has the following format:

(loop <body>)

Example: count the number of elements in a list.

* (setf list-1 '(a b c d e))

(A B C D E)

* (setf count-elements 0)

0

* (loop (when (endp list-1) (return count-elements))

(setf count-elements (+ 1 count-elements))

(setf list-1 (rest list-1)))

5

Page 22: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

The PROG1 and PROGN primitives allow several forms to be viewed as a group in which forms are evaluated sequentially. Prog1 has the following form:

(prog1 <form 1> <form 2> … <form n>).

All forms are evaluated, and prog1 returns the value of the first form.

Progn has the following form:

(progn <form 1> <form 2> … <form n>).

All forms are evaluated, and progn returns the value of the last form.

Example:* (prog1 (eql 'a 'a) (setf a 5))

T

* (progn (eql 'a 'a) (setf a 5))

5

Page 23: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

The FORMAT primitive: produces more elegant printing than PRINT

Example:* (format t "Hi there!")Hi there! ; the t argument says that the

output should be ;

printed at the terminal. The returned value is NIL ; NIL

To print a string on a new line, the ~% directive should be placed at thebeginning of the string:

* (format t "~%Hi there!")Hi there!NIL

The TERPRI primitive also moves the cursor to a new line. For example: * (prog1 (terpri) (format t "Hi"))

Hi NIL

The ~& directive tells format to print on a new line, if it is not already on a new line, while the ~a directive tells format to insert the value of an additional argument which appears after format’s string.

Page 24: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

More examples

* (progn (format t "~%Student name: ~a" name) (format t "~&Major: ~a" major))Student name: ANNAMajor: CSNIL

* (format t "~%Student name: ~10a Major: ~10a" name major)

Student name: ANNA Major: CS

NIL

* (format t "~%Student name: ~20a Major: ~10a" name major)

Student name: ANNA Major: CS

NIL

Page 25: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

The Read primitive reads a value from the keyboard

Examples:* (read)hello ; the

input is typed immediately after read.

HELLO

* (setf greetings (read))hi ; the input can be assigned to a variable.

HI

* greetings

HI

* (setf greetings (read))(hi there!) ; if the input contains more

(HI THERE!) ; than one symbol it must be enclosed in a list.

* greetings

(HI THERE!)

Page 26: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

Print and read used in combination allow for a more informative dialog

Example:* (prog1 (print '(Please enter your name))

(setf name (read))

(print (append '(hi) (list name) '(how are you today?))))

(PLEASE ENTER YOUR NAME) neli

(HI NELI HOW ARE YOU TODAY?)

(PLEASE ENTER YOUR NAME)

* name

NELI

Do not use “:” in the print argument; for example, name: will be understood

as a reference to a package which does not exist and will result in an error.

Page 27: The Case primitive: matches the evaluated key form against the unevaluated keys by using eql The general format of case is the following: (case (... )

Read, Terpri and Format in combination can also be used for a more informative dialog

Example:* (prog1 (terpri)

(format t "Please enter your name : ")

(setf name (read)) (terpri)

(format t "Hi ~a how are you today?" name))

Please enter your name : neli

Hi NELI how are you today?

NIL

Notice the space between “name” and “:” in the format statement.