710 likes | 853 Views
LISP. COMP 205 – Week 12 Dr. Chunbo Chu. Last week. Install Lisp in a Box loop read in an expression from the console; evaluate the expression; print the result of evaluation to the console; end loop. REPL. Valid objects (S-expressions). Atoms : numbers : (real 1.0, integer 1)
E N D
LISP COMP 205 – Week 12 Dr. Chunbo Chu
Last week • Install Lisp in a Box • loop • read in an expression from the console; • evaluate the expression; • print the result of evaluation to the console; end loop. • REPL
Valid objects (S-expressions) • Atoms: • numbers: (real 1.0, integer 1) • symbols: a consecutive sequence of characters (no space) • e.g., a, x, price-of-beef. • two special symbols: T and NIL for logical true and false. • strings: a sequence of characters bounded by double quotes • e.g., "this is red". • (Note: LISP is case insensitive) • Lists: a list of atoms and/or lists, bounded by "(" and ")“, • e.g., (a b c), (a (b c)) • top elements of a list • example: top elements of list (a b c) are a, b, and c • top elements of list (a (b c)) are a and (b c) • nil: empty list, same as ().
2. Function calls • also a list • use prefix notation: (function-name arg1 ... argn) • returns function value for the given list of arguments • functions are either provided by Lisp function library or defined by the user. • Examples: • >(+ 1 3 5) • 9 • >(/ 3 5) • 3/5 • >(/ 3.0 5) • 0.59999999999999998 • >(sqrt 4) • 2
First Lisp program ( defun hello () (format t “Hello World!”)) • Use Emacs to create a new file (C-x C-f)containing the above code. • Save the file (C-x C-s) under name hello.lisp • Switch back the SLIME buffer and load the program by: (load “hello.lisp) • Run your program: (hello) • Type q to leave the debugger if you have any error.
first • rest • function nesting
car • cdr • cadr • caddr • nthcdr • butlast • cons • append
2) Predicates (a special function which returns NIL if the predicate is false, T or anything other than NIL, otherwise) =, >, <, >=, <= for numerical values; equal, eq, for others (symbols, lists, etc.) tests if x is a atom tests if x is a list also numberp, symbolp, null predicates >(equal ‘a (car L)) T >(< x y) NIL >(= x y) T >(equal ‘x ‘y) NIL >(atom x) T >(atom L) NIL >(atom (car L)) T >(listp x) NIL >(listp L) T >(numberp ‘x) NIL >(numberpx) T >(symbolp ‘x) T >(symbolpx) NIL
>(null NIL) T >(null L) NIL >(null x) NIL 3) Set operations ( a list can be viewed as a set whose members are the top elements of the list) >(member 'b L) ; test if symbol b is a member (a top element) of L (B C) ; if yes, returns the sublist of L starting at the ; first occurrence of symbol b >(member ‘b (cons 'b L)) (B A B C) >(member x L) NIL ; if no, returns NIL >(union L1 L2) ; returns the union of the two lists >(intersection L1 L2) ; returns the intersection of the two lists >(set-difference L1 L2) ; returns the difference of the two lists Set operations
Defining New Functions (defun name (parameter*) "Optional documentation string." body) • Convention: you construct compound names with hyphens rather than underscores or inner caps. • Thus, frob-widget is better Lisp style than either frob_widget or frobWidget. • When a parameter list is a simple list of variable names, the parameters are called required parameters
Optional Parameters • Place the symbol &optional followed by the names of the optional parameters. (defunfoo (a b &optional c d) (list a b c d)) • When the function is called, arguments are first bound to the required parameters. (foo 1 2) (1 2 NIL NIL) (foo 1 2 3) (1 2 3 NIL) (foo 1 2 3 4) (1 2 3 4)
Non-NIL default value • Replace the parameter name with a list containing a name and an expression. • The expression will be evaluated only if the caller doesn't pass enough arguments to provide a value for the optional parameter. (defunfoo (a &optional (b 10)) (list a b)) (foo 1 2) (1 2) (foo 1) (1 10)
More flexibility: • (defun make-rectangle (width &optional (height width)) ...)
Rest Parameters • Functions need to take a variable number of arguments. • E.g. (+), (+ 1), (+ 1 2), (+ 1 2 3), … • A catchall parameter after the symbol &rest. • Any arguments remaining after values have been doled out to all the required and optional parameters are gathered up into a list that becomes the value of the &rest parameter. (defun format (stream string &rest values) ...) (defun + (&rest numbers) ...)
Keyword Parameters • Suppose you have a function that takes four optional parameters. • Now suppose that most of the places the function is called, the caller wants to provide a value for only one of the four parameters. • After any required, &optional, and &rest parameters you include the symbol &key and then any number of keyword parameter specifiers.
(defunfoo (&key a b c) (list a b c)) • (foo) (NIL NILNIL) • (foo :a 1) (1 NIL NIL) • (foo :b 1) (NIL 1 NIL) • (foo :c 1) (NIL NIL 1) • (foo :a 1 :c 3) (1 NIL 3) • (foo :a 1 :b 2 :c 3) (1 2 3) • (foo :a 1 :c 3 :b 2) (1 2 3)
(defunfoo (&key (a 0) (b 0 b-supplied-p) (c (+ a b))) • Mixing Different Parameter Types • Whenever more than one flavor of parameter is used, they must be declared in order : first the names of the required parameters, then the optional parameters, then the rest parameter, and finally the keyword parameters.
Function Return Values • The default behavior: return the value of the last expression evaluated as the function’s own return value. • The RETURN-FROM special operator to immediately return any value from the function • The first "argument" is the name of the block from which to return. (defunfoo (n) (dotimes (i 10) (dotimes (j 10) (when (> (* i j) n) (return-from foo (list i j))))))
Data structures • assoc
make-array • aref • defstruct
4) Conditional • >(cond (<test-1> <action-1>) • . • . • . • (<test-k> <action-k>)) • each (<test-i> <action-i>) is called a clause; • if test-i (start with i=1) returns T (or anything other than NIL), • this function returns the value of action-i; • else, go to the next clause; • usually, the last test is T, which always holds, meaning otherwise. • cond can be nested (action-i may contain (cond ...)) conditional
Now, having basic functions, defun and cond we can define any Lisp function. Examples. 5. Define functions (heavy use of recursive definitions) (defun func-name (arg-1 ... Arg-n) func-body) examples: (defun member (x L) (cond ((null L) nil) ; base case 1: L is empty ((equal x (car L)) L) ; base case 2: x=first(L) (t (member x (cdr L))) ; recursion: test if x is in rest(L) )) (defun intersection (L1 L2) (cond ((null L1) nil) ((null L2) nil) ((member (car L1) L2) (cons (car L1) (intersection (cdr L1) L2))) (t (intersection (cdr L1) L2)) )) Example: (intersection '(a b c) '(b a b c)) returns (a b c) (intersection '(b a b c) '(a b c)) returns (b a b c) member intersection
(defun set-difference (L1 L2) • (cond ((null L1) nil) • ((null L2) L1) • ((not (member (car L1) L2)) • (cons (car L1) (set-difference (cdr L1) L2))) • (t (set-difference (cdr L1) L2)) • )) • Define functions iteratively. • (dolist (x L result) body) • for each top level element x in L, do body(x); • x is not equal to an element of L in each iteration, but rather x takes an element of L as its value; • (dotimes (count n result) body) • ; do body n times. count starts with 0, ends with n-1 • Note: result is optional, to be used to hold the computing result. • If result is given, the function will return the value of result, • returns NIL, otherwise. (may change global variables as side effects.) dolist dotimes
Activity • Write a function sum to calculate the sum of all numbers in a list
Various definitions of SUM dolist (defun sum1 (L) (setq y 0) (dolist (x L y) (setq y (+ y x)))) (defun sum2 (L) (setq y 0) (dolist (x L y) (setq y (+ y (eval x))))) (defun sum3 (L) (setq y 0) (dotimes (count (length L) y) (setq y (+ y (nth count L))) )) defun sum4 (L) (setq y 0) (dotimes (count (length L) y) (setq y (+ y (eval (nth count L)))) )) >(setq L1 '(1 2 3)) (1 2 3) dotimes >(sum1 L1) 6
>(setq L1 '(1 2 3)) (1 2 3) >(setq L2 '(a b c)) (A B C) >(dotimes (count 3) (set (nth count L2) (nth count L1))) NIL >a 1 >(sum1 L1) 6 >(sum1 L2) Error: … >(sum2 L2) 6 >(sum3 L1) 6 >(sum3 L2) Error: … >(sum4 L2) 6
if statement • (if <test> <then> <else>) > (defun abs (a) (if (> a 0) a (- a))) ABS > (abs 2) 2 > (abs -3) 3
Logical operators • and, or > (and NIL T) NIL > (and T 2 3) 3 > (or nil (= 5 4)) NIL > (or nil 5) 5
Recursion • Recursive function definitions are very common in LISP > (defun factorial (num) (cond ((<= num 0) 1) (t (* (factorial (- num 1)) num)))) FACTORIAL > (factorial 4) 24
Variable • Common Lisp is dynamically typed—type errors are detected dynamically. • A variable can hold values of any type and the values carry type information that can be used to check types at runtime. • Common Lisp is a strongly typed language in the sense that all type errors will be detected. (+ “hello” 1)
Introducing variables • (defunfoo (x y z) (+ x y z)) • LETspecialoperator (let (variable*) body forms) • Example: (let ((x 10) (y 20) z) ...) • Within the body of the LET, the variable names refer to the newly created bindings. • After the LET, the names refer to whatever, if anything, they referred to before the LET.
Nested bindings (defunfoo (x) (format t "Parameter: ~a~%" x) ; |<------ x is argument (let ((x 2)) ; | (format t "Outer LET: ~a~%" x) ; | |<---- x is 2 (let ((x 3)) ; | | (format t "Inner LET: ~a~%" x)) ; | | |<-- x is 3 (format t "Outer LET: ~a~%" x)) ; | | (format t "Parameter: ~a~%" x)) ; | • The value of the last expression in the body is returned as the value of the LET expression.
Binding forms • Any construct that introduces a new variable name that's usable only within the construct is a binding form. • More example? • (dotimes (x 10) (format t "~d " x))
LET* (let* ((x 10) (y (+ x 10)) ) (list x y)) • But not this (let ((x 10) (y (+ x 10))) (list x y))
Global variables • Two ways to create global variables: DEFVAR and DEFPARAMETER. • Global variables are conventionally named with names that start and end with *. (defvar *count* 0 "Count of widgets made so far.") • The difference : • DEFPARAMETER always assigns the initial value to the named variable • DEFVAR does so only if the variable is undefined. • A DEFVAR form can also be used with no initial value to define a global variable without giving it a value. Such a variable is said to be unbound.
Other data structures • Vectors come in two flavors • Fixed-size vectors are a lot like arrays in a language such as Java: a thin veneer over a chunk of contiguous memory that holds the vector's elements. • Resizable vectors, on the other hand, are more like arrays in Perl or Ruby, lists in Python, or the ArrayList class in Java: they abstract the actual storage, allowing the vector to grow and shrink as elements are added and removed.
Vector • To make fixed-size vectors containing specific values with the function VECTOR (vector) #() (vector 1) #(1) (vector 1 2) #(1 2) • MAKE-ARRAY is more general than VECTOR since you can use it to create arrays of any dimensionality as well as both fixed-size and resizable vectors. • One required argument to MAKE-ARRAY is a list containing the dimensions of the array. • (make-array 5 :initial-element nil) #(NIL NILNILNILNIL)
Vector • A resizable vector uses fill pointer to store the number of elements actually stored in the vector. (make-array 5 :fill-pointer 0) #() • To add an element to the end of a resizable vector, use the function VECTOR-PUSH. • The function VECTOR-POP returns the most recently pushed item, decrementing the fill pointer in the process.
(defparameter *x* (make-array 5 :fill-pointer 0)) (vector-push 'a *x*) 0 *x* #(A) (vector-push 'b *x*) 1 *x* #(A B) (vector-push 'c *x*) 2 *x* #(A B C) (vector-pop *x*) C *x* #(A B) (vector-pop *x*) B *x* #(A) (vector-pop *x*) A *x* #()
Vector • To make an arbitrarily resizable vector, you need to pass MAKE-ARRAY another keyword argument: :adjustable. (make-array 5 :fill-pointer 0 :adjustable t) #() • To add elements to an adjustable vector, you use VECTOR-PUSH-EXTEND, which works just like VECTOR-PUSH except it will automatically expand the array if you try to push an element onto a full vector.
Activity • Create an array of 5 elements, fill-pointer as 0 • Use a loop to populate the array with 5 numbers • Then calculate the sum of the numbers
(defparameter *x* (make-array 5 :fill-pointer 0)) • (defparameter*sum* 0) • (dotimes (i 5 *x*) (vector-push i *x*)) • (dotimes (i 5 *x*) (setq *sum* (+ *sum* (vector-pop *x*))))