Lisp, part 3

Last modified: "February 16, 2000 09:21:20 by evett"

Whenever you are in doubt about something below, you should first check in a Lisp reference, like Graham's book, or the CMU on-line lisp documentation, for an explanation. If you're still confused, see me, I'll be glad to help.

--Matt

ARRAYS:

> (setf arr (make-array '(2 3) :initial-element nil)) (setf arr (make-array '(2 3) :initial-element nil)) #2A((NIL NIL NIL) (NIL NIL NIL)) > (setf (aref arr 0 0) 'b) (setf (aref arr 0 0) 'b) B > arr #2a((b nil nil) (nil nil nil))

STRUCTURES:

(defstruct point x y) defines accessor functions POINT-X and POINT-Y, and creation function MAKE-POINT, and predicate POINT-P. [Defines other stuff, too. See Graham or Steele for info.] Defstruct is a lot like C++'s class statement: it defines a datatype, but does not create any instances of that new type. ;============================================================

FUNCTIONS AS ARGUMENTS. FUNCTIONS AS FIRST CLASS OBJECTS.

(defun sum-of-ints (n) (do ((i n (1- i)) (result 0 (+ i result))) ((zerop i) result))) What if we want to modify it from a simple sigma to a sigma of squares, or cubes, etc? ?What's wrong with: (defun general-sum (func n) (do ((i n (1- i)) (result 0 (+ (func i) result))) ((zerop i) result))) Functions without names. (defun red-p (x) (equal x 'red)) > (red-p 'red) T ((lambda (x) (equal x 'red)) 'red) >((lambda (x) (equal x 'red)) 'blue) NIL > ((lambda (x) (equal x 'red)) 'red) T > (lambda (x) (equal x 'red)) >>Error: The function LAMBDA is undefined value of FUNC vs. function def of FUNC LAMBDA is not a function. The symbol LAMBDA + its accoutrements (a lambda expression) *is* a function. A DEFUN merely associates a function description (called a lambda expression) with a particular symbol. symbol-function is a way to access this description. > (symbol-function 'red-p) ;Access the function value of symbol RED-P # ===========

APPLY and FUNCALL (Graham 2.14)

(apply #'+ '(1 2 3)) (+ 1 2 3) what's wrong with? (apply #'car '(a b c)) (apply #'car '((a b c))) (car '(a b c)) For apply, args are specified as a list. Now we can implement our general function (defun general-sum (func n) (do ((i n (1- i)) (result 0 (+ (apply func (list i)) result))) ;Why LIST? ((zerop i) result))) (defun squared (x) (* x x)) > (general-sum #'squared 3) ; "#'" is more correct than just "'". 14 Really, #' is an abbreviation for FUNCTION, as ' is an abbrev for QUOTE > (general-sum (function squared) 3) 14 But why define a simple little function like squared, when all we need is a temporary? LAMBDA to the rescue. > (general-sum #'(lambda (x) (* x x)) 3) 14 Note that (general-sum '(lambda (x) (* x x)) 3) won't work because the first argument is a mere list, not a function.

FUNCALL takes as many args as appropriate

(defun general-sum (func n) (do ((i n (1- i)) (result 0 (+ (funcall func i) result))) ;Note: no LIST needed ((zerop i) result))) ========================

MAPPING FUNCTIONS -- related to iteration. (Graham 3.7)

? (mapcar #'atom '(a b (nil) c (d e))) (T T NIL T NIL) ? mapc is like mapcar, but doesn't retain return values. It always returns its second arg. Used for side-effects of applications. maplist is cdr equivalent of mapcar (returns list of results) (maplist #'cons '(a b) '(x y)) (maplist #'list '(a b) '(x y)) mapl is cdr equivalent of mapc ;;Below is an example well worth understanding, pulling together several important concepts. (defun how-many-reds (list) (apply '+ (mapcar '(lambda (x) (if (equal x 'red) 1 0)) list))) > (how-many-reds '(a b red c d red g)) (how-many-reds '(a b red c d red g)) 2 > ;========================================

CLOSURES: static variables within a function or set of functions. Local variables whose values persist between function invocations.:

(defun adder (c) "Return a function (a closure, too) that adds c to its argument." #'(lambda (x) (+ c x))) ;;; Any free variables remain defined so long as anything might refer to them. As long ;;; as the above lambda expression exists, then, a version of the variable C must remain ;;; defined. Graham: "If a function is defined within the scope of a lexical variable, ;;; it can continue to refer to that variable, even if it is returned as a value outside ;;; the context where the variable was created." "A function that refers to a [so ;;; called] free lexical variable is called a _closure_. The variable must persist as ;;; long as the function does." (setq add-5 (adder 5)) (funcall add-5 2) (funcall add-5 -1) (mapcar (adder 3) '(1 3 10)) (mapcar (adder 3) '(4 8 7)) (defun bank-account (balance) "Open a bank account starting with the given balance." #'(lambda (action amount) (cond ((equal action 'deposit) (setf balance (+ balance amount))) ((equal action 'withdraw) (setf balance (- balance amount)))))) (setf your-account (bank-account 250.00)) (setf my-account (bank-account 500.00)) (funcall my-account 'withdraw 75.00) (let ((x 0)) (defun bob (z) (setq x (+ x z)))) > (bob 2) (bob 2) 2 > (bob 5) (bob 5) 7 > ;;; The nice thing about BOB as opposed to BANK-ACCOUNT is that BOB can be ;;; compiled. BANK-ACCOUNT cannot, under most Lisp implementations, it exists ;;; as a lambda expression, and must be interpreted at each invocation. ;========================================

INPUT/OUTPUT

Lisp's FORMAT is a lot like C's PRINTF (for old-timers, anyway! Many of you who have been raised on C++, rather than C, have always used operator<<, rather than printf.) The first argument to FORMAT is either T, to send the output to the screen, or is a stream variable, to send the output to the file bound to that stream via an OPEN command. The second argument to FORMAT is a control string, which contains directives that determine how the subsequent arguments to the FORMAT will be displayed. The "~" expressions are effectively placeholders for the values of the subsequent arguments. ~A ~D ~% ~F, etc. (format <stream or t> "control string" [<args>]) ~A is used to print an s-expression. > (format t "This is the value of B:~a~%" a) This is the value of B:37 NIL > PRINT works for simple terms printed on one line, but is otherwise to be avoided.

READING/INPUT:

(read) returns the next s-expression. > (loop (format t "Enter a number: ") (let ((in (read))) (if (equal in 'end) (return nil)) (format t " squared is ~d.~%" (* in in)))) Enter a number: 5 5 squared is 25. Enter a number: 5 6 5 6 squared is 25. Enter a number: squared is 36. Enter a number: end end NIL > You should be careful, when using READ, that your input or input file does not contain commas, apostrophes, etc., as these have special value when read as part of s-exps. If you want to read an input line as a string, and not as an s-exp, you can use READ-LINE.

File I/O

(let ((my-stream (open "myFile" :direction :output))) (format my-stream "~%howdy doody~%") (close my-stream))
Dealing with EOF:
The READ statement can take as many as 3 arguments: a stream to read from, a boolean value indicating whether EOF should be treated as an error, and a value to be returned when EOF is reached. Using this trick it is possible to read and output all the s-expressions in a file (with a few restrictions!) (let ((my-stream (open "myFile" :direction :input)) val) ; Value of current s-exp (loop (setq val (read my-stream nil 'EOF)) (if (eq val 'EOF) (return) ; Leave the loop upon EOF (format t "~a" val))) ; Else, print the just read s-expression (close my-stream))
Global variables affecting I/O
*read-base* ; Controls the base of numeric number representation. (Default is 10, of course). (let ((*read-base* 8)) (print (read))) ;Explain all output *print-base* ; Controls the output base of numeric number representation. (Default is 10, of course). (let ((*print-base* 8)) (print (read))) ;Explain all output *print-length* ; When printing a list, if # of elements in list exceeds this, "..." is used. *print-level* ; When printing a list, if depth of list nesting exceeds this, "#" is used. *standard-output* ; Stream for standard output (i.e., console output) *standard-input* ; Stream for standard input (i.e., console input) Generally we mess with these variables via a (temporary) rebinding via LET. This effectively makes changes to them only temporary (for the duration of the LET). (setq bob (do ((x 100 (1- x)) (result nil (cons x result))) ((zerop x) result))) > (let ((*print-length* 10)) (print bob) nil) ; Why do you think I put "nil" here? (let ((*print-length* 10)) (print bob) nil) (1 2 3 4 5 6 7 8 9 10 ...) NIL > ======================================================

DEBUGGING:

In error/break loop of Unix's Lisp, use :h or :? to see list of commands. Other Lisp implementations use different debugging environments. The Unix Lisp environment is fairly rudimentary, but provides most of the same functionality as others, just not so pretty a user-interface.

TRACE

(defun factorial (n) ;Recursive bindings protect us. (if (zerop n) 1 (* n (factorial (1- n))))) (trace factorial) (factorial 3) ;Try it and see! (untrace factorial) > (step (factorial 3)) (FACTORIAL 3) -> :n :n (FUNCTION FACTORIAL) -> ? ? :n Evaluate current expression in step mode. :s Evaluate current expression without stepping. :u Evaluate current expression without stepping and go up one level of stepping. :x Finish evaluation, but turn Stepper off. :p Print current expression. :pp Pretty-print current expression. :b Enter the Debugger. :q Exit to Top Level. :h or ? Print this text. (FUNCTION FACTORIAL) -> :n :n # 3 = 3 1 Enter FACTORIAL 3 (BLOCK FACTORIAL (IF (ZEROP N) 1 (* N #))) -> :n :n (IF (ZEROP N) 1 (* N (FACTORIAL #))) -> :n :n (ZEROP N) -> :s :s NIL (* N (FACTORIAL (1- N))) -> :s :s | 2 Enter FACTORIAL 2 | 3 Enter FACTORIAL 1 | | 4 Enter FACTORIAL 0 | | 4 Exit FACTORIAL 1 | 3 Exit FACTORIAL 1 | 2 Exit FACTORIAL 2 6 6 6 1 Exit FACTORIAL 6 6 6 >