Lisp, part 3
Last modified: "September 3, 1996 17:43:07 by matt"
Whenever you are in doubt about something below, you should first check in
Graham 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.]
;============================================================
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. The control string contains directives
that determine how the arguments to the FORMAT will be displayed.
~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
>
Redirecting I/O
(let ((my-stream (open "my-file" :direction :output)))
(format my-stream "~%howdy doody~%")
(close my-stream))
Dealing with EOF:
Slightly more complex READ
(let ((my-stream (open "my-file" :direction :input)))
(format t "~a" (read my-stream nil 'EOF))
(close my-stream))
*read-base*
(let ((*read-base* 8)) (print (read))) ;Explain all output
*print-base*
(let ((*print-base* 8)) (print (read))) ;Explain all output
*print-length*
*print-level*
*standard-output*
*standard-input*
Generally we mess with these variables via a (temporary) rebinding via LET
(setq bob
(do ((x 100 (1- x))
(result nil (cons x result)))
((zerop x) result)))
> (let ((*print-length* 10)) (print bob) nil)
(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
>