360 likes | 492 Views
Processing Symbolic Data. More Standard Functions. Some more useful functions and predicates (symbolp s ) T for atomic symbol s s (numberp s ) T for numbers s (consp s ) T for cons value s ( i.e . it has car a nd cdr) (listp s ) T for list s ( maybe empty ) Selectors/accessors
E N D
More Standard Functions • Some more useful functions and predicates • (symbolps)Tfor atomic symbolss • (numberps)T fornumbers s • (consps)T for cons values(i.e. it has car and cdr) • (listps)T forlists(maybe empty) • Selectors/accessors • cxx...xr selectors - combinationsof car/cdr • (caar x) = (car (car x)) (cadr x) = (car (cdr x))... • Similarly caaar, caadr, ..., cdddr, caaaar, caaadr, ..., cddddr • (length s) how many elements in s • (nth ns) n-thelement of list s (leftmost is 0th!) • (nthcdr ns)n-th cdr • (last s)last cons cell • (butlast s)list s without the last element • (butlast sn)n elements from the end removed
All those are standard, but could be defined as follows • (defun nth (N S) ; N-thelement of list S • (cond ((= N 0) (car S)) ;car is 0th! • (T (nth (1- N) (cdr S))) • ) ) • (defun nthcdr (N S) ; n-th cdr • (cond ((= N 0) S) • (T (nthcdr (1- N) (cdr S))) • ) ) • (defun last (S) ; the last cons cell • (cond ((null S) NIL) • ((null (cdr S) S) • (T (last (cdr S))) • ) ) • (defun butlast (S) ; list S without the last element • (cond ((or (null S) (null (cdr S)) NIL) • (T (cons (car S) • (butlast (cdr S)) • ) ) ) )
Still More Functions ... • (member Elm Lst) looks for Elm in Lst, returns the part starting • with Elm • (remove Elm Lst) deletes Elm from Lst (returns a copy) • (subst New Old Lst) substitutes New for Old in Lst • (returns a copy) • (nsubst New Old Lst)substitutes New for Old in Lst • (returns a modified Lst) • (reverse Lst) reverses Lst (returns a fresh list)
(defun member (Elm Lst) • (cond ((null Lst) NIL) • ((eql Elm (car Lst)) Lst) ; eq, eql, equal ... • (T (member Elm (cdr Lst))) • ) ) • (defun remove (Elm Lst) • (cond ((null Lst) NIL) • ((eql Elm (car Lst)) ; Elm found? • (remove Elm (cdr Lst))) ; remove from rest • (T (cons (car Lst) ; else copy car • (remove Elm (cdr Lst))) • ) ) • (defun subst (New Old Lst) • (cond ((null Lst) NIL) • ((eql Old (car Lst)) • (cons New • (subst New Old (cdr Lst)))) • (T (cons (car Lst) • (subst New Old (cdr Lst)))) • ) ) ; what about substitution in all levels ???
(defun nsubst (New Old Lst) • (cond ((null Lst) NIL) • ((eql Old (car Lst)) • (setf (car Lst) New) • (setf (cdr Lst) (nsubst New Old (cdr Lst))) • Lst) • (T (setf (cdr Lst) (nsubst New Old (cdr Lst))) • Lst) • ) ) ; calls for some improvement ... • ; How shall we reverse a list ??? • (defun reverse (Lst) • (if (null Lst) NIL • (append (reverse (cdr Lst)) • (list (car Lst))) • ) ) • (defun append (X Y) • (if (null X) Y • (cons (car X) • (append (cdr X) Y)) • ) )
; Can we do reverse any better ??? • (defun reverse (Lst) • (rev-iter Lst Nil)) • (defun rev-iter (Lst Acc) • (if (null Lst) Acc • (rev-iter (cdr Lst) (cons (car Lst) Acc)) • ) )
CL-USER 1 > (rev S) • 0 REV > ... • >> LST : (A B C) • 1 REV-ITER > ... • >> LST : (A B C)>> ACC : NIL • 2 REV-ITER > ... • >> LST : (B C)>> ACC : (A) • 3 REV-ITER > ... • >> LST : (C) >> ACC : (B A) • 4 REV-ITER > ... • >> LST : NIL>> ACC : (C B A) • 4 REV-ITER < ... • << VALUE-0 : (C B A) • 3 REV-ITER < ... • << VALUE-0 : (C B A) • 2 REV-ITER < ... • << VALUE-0 : (C B A) • 1 REV-ITER < ... • << VALUE-0 : (C B A) • 0 REV < ... • << VALUE-0 : (C B A) • (C B A)
In a simplified presentation: • (rev (A B C)) • (rev-iter (A B C) () ) • (rev-iter (B C) (A) ) • (rev-iter (C) (B A) ) • (rev-iter () (C B A) ) (C B A) • returned (C B A) • returned (C B A) • returned (C B A) • returned (C B A) • returned (C B A)
eval, apply and funcall • > (defvar S '(+ 1 2)) • > S • (+ 1 2) • > (eval S) • 3 • > S • (+ 1 2) • As a result: any (properly constructed) expression created during program execution can be evaluated again • (eval Form ) • (fn arg1 ... agrn)(apply Fn ArgList ) • (funcall Fn Arg1 ... Argn )
eval, apply and funcall • Remind the summation procedure • (defun sum (S) • (if (null S) 0 • (+ (car S) (sum (cdr S))) • ) ) • (defun app-sum (S) (apply #'+ S)) • What if we want to sum-up results of some function • S = (a1 a2 a3 ... an) sum-f = ff(a1) + ff(a2) + ... + ff(an) • (defun sum-f (FF S) • (if (null S) 0 • (+ (FF (car S)) • (sum-f FF (cdr S))) • ) )
eval, apply and funcall • Remind the summation procedure • (defun sum (S) • (if (null S) 0 • (+ (car S) (sum (cdr S))) • ) ) • (defun app-sum (S) (apply #'+ S)) • What if we want to sum-up results of some function • S = (a1 a2 a3 ... an) sum-f = ff(a1) + ff(a2) + ... + ff(an) • (defun sum-f (FF S) • (if (null S) 0 • (+ (funcall FF (car S)) ; !!! IMPORTANT !!! • (sum-f FF (cdr S))) • ) )
R e c u r s i o n • Lisp = Recursion • Taxonomy of recursion • nested recursion • (defun Ack (m n); (Ack 4 5) stack overflow • (cond ((zerop m) (1+ n)) • ((zerop n) (Ack (1- m) 1)) • (T (Ack (1- m) (Ack m (1- n)))))) • tree recursion • (defun Fib (n); (Fib 35) took 26.328 s • (if (< n 2)n • (+ (Fib (- n 1))(Fib (- n 2))) • )) nested recursive call two recursive calls
linear recursion • (defun linLen (s) • (if (null s) 0 • (1+ (linLen (cdr s))) • ) ) • tail recursion • (defun trLen (s) (tailLen s 0)) • (defun tailLen (s acc) • (if (null s) acc • (tailLen (cdr s) (1+ acc)) • ) ) • Tail recursion is the most efficient and can be transformed into explicit iteration mechanically. just one recursive call one recursive call, no pending operations
General rule of recursive design • first test for trivial cases (such as 0, 1, NIL, atom, ... ) • then develop branches with recursive calls in which the procedure is applied to reduced arguments (1- N), (cdr S), ... • Example - copyinga list (highest level only) • (defun Copy (S) • (if (null S) NIL • (cons (car S) (Copy (cdr S))) )) • Now copying at all levels • (defun CopyAll (S) • (cond ((null S) nil) • ((atom S) S) • ((cons (CopyAll (car S)) • (CopyAll (cdr S)))) ))
Sorting Recursively • Insertion Sortalgorithm • (defun Isort (S) (sort-iterSNIL)) • (defun sort-iter (SSeq) ; Seq is already sorted part • (if (null S) Seq; sorting finished • (sort-iter (cdr S); sort the rest • (Insert (car S) Seq))) • ) ) • (defun Insert (XSeq) ;insert Xinto Seq • (cond ((null Seq) (list X)) • ((< X (car Seq)) (cons XSeq)) • (T (cons (car Seq)(Insert X (cdr Seq)))) • ) ) • (defun Isort (S) (sort-iterS));second arg missing • (defun sort-iter (S&optional Seq) ;Seq is optional • ...
Sorting Iteratively • Insertion Sortalgorithm • (defun Isort (S) (sort-iter(cdr S)(list (car S)))) • (defun sort-iter (SSeq) ; Seq is already sorted part • (dolist (E SSeq) ; going through list S • (setf Seq (Insert ESeq)); insert E into Seq • ) ) • (defun Insert (XSeq) ;insert Xinto Seq <> nil !!! • (do ((SSeq (cdr S)) (P nil S)(LX (list X))) • ((or (null S) (< X (car S)) • (setf (cdr LX) S) • (if (null P) • (setf Seq LX) • (setf (cdr P) LX)) • Seq • ) ) P S 1 2 4 5 Seq 3 LX
Sets as Lists • set = list of atoms, no repetition • typical set operations - union, intersection, difference, ... • (defun Union (A B) ; A B • (cond ((null A) B) • ((member (car A) B) (Union (cdr A) B)) • (T (cons (car A) (Union (cdr A) B))) )) • (defun Intersection (A B) ; A B • (cond ((null A) NIL) • ((member (car A) B) • (cons (car A) (Intersection (cdr A) B))) • (T (Intersection (cdr A) B)) )) • (defun Difference (A B) ; A - B • (cond ((null A) NIL) • ((member (car A) B) (Difference (cdr A) B)) • (T (cons (car A) (Difference (cdr A) B))) ))
Recursively Searching Subsequences • Given sequences p = (p1 p2 ... pn) and q = (q1 q2 ... qm)find the first occurrence of p in q (if any). • p =(A B B A) q =(A B C A B B A C A) → (A B B A C A) q =( A BC A B B A C A ) p =( A BB A ) we test whether p is a prefix of q, if not advance to the next position in q q =( A B C A B B A C A ) p =( A B B A ) q =( A B C A B B A C A ) p =( A B B A ) q =( A B C A B B A C A ) p =( A B B A )
(defun sub-seq1 (p q) • (cond ((test-prefix p q) q); if p is prefix of q • ((null q) NIL) ; p was not found • (T (sub-seq1 p (cdr q))) ; move to the right • ) ) • (defun sub-seq2 (p q) ; first compare list lengths • (cond ((> (length p) (length q)) NIL) • ((test-prefix p q) q ; if p is prefix of q • (T (sub-seq2 p (cdr q))) ; step to the right • ) ) • ? Which version is faster and why ? • (defun test-prefix (pq) • (cond ((null p)) • ((null q) NIL) • ((eq (car p) (car q)) • (test-prefix (cdr p) (cdr q))) • ) ) • (defun test-prefix (&key (:prefix p)(:sequence q)) • ... (test-prefix :sequence (cdr q) :prefix (cdr p)))
Tricks withArguments • Variable number of aguments ... &rest: • (defun min-max1 (S) ; S is a list of numbers • (list (apply #'min S) (apply #'max S))) • (defun min-max2 (&rest S) ; numbers only • (list (apply #'min S) (apply #'max S))) • (min-max1 '(4 2 6 3 7 1)) • (min-max2 4 2 6 3 7 1) • Keywords we have already seen • &optionalx(xdefault )(xdefault flag ) • &keyx(xdefault )(xdefault flag ) key is :x • ((:yx)default )((:yx)default flag )
Multiple Values • Lisp functions can return ANY number of values (not just one) • > (values 1 2 3) ; or (value-list '(1 2 3)) • 1 • 2 • 3 • (multiple-value-setq VarList ValueForm ) > (multiple-value-setq (a b c) (values 1 2 3)) ; a=1, b=2, c=3 • (multiple-value-bind VarList ValueForm Form1 ... ) > (multiple-value-bind (a b c) (values 1 2 3) (list c b a)) (3 2 1)
Multiple Values • (multiple-value-call Fn Form1 Form2 ... ) • (multiple-value-call #'list 1 (values 2 3 4) 5) • (1 2 3 4 5) • (multiple-value-list Form ) • (multiple-value-list (values 1 2 3)) (1 2 3) • (multiple-value-prog1 Form1 Form2 ... ) • (multiple-value-prog1 (values 1 2 3) (cons a b)) • 1 • 2 • 3
Programming Example(adopted from http://fare.tunes.org/cgi-bin/viewcvs.cgi/fare/lisp/fibonacci.lisp) • Back to Fibonacci – how to improve efficiency: • (defun AFib (n) (FibTail n 0 1)) • (defun FibTail (n a b) • (if (zerop n) a) • (FibTail (1- n) b (+ a b)) ) ) • (AFib 50) ; stack overflow ... • (defun BFib (n) ; iterative-only design ... • (loop repeat n • with p = 0 with q = 1 • do (psetq p q • q (+ p q)) • finally (return p))) • (FastFib 100000) ; calculated in 2.75 s • Good enough? Not?? Right!
Programming Example (cont. 1) • Consider the following simple design pattern for iteration: • (defun iter-1 (f n &rest args) • (if (zerop n) (apply #'values args) • (apply #'iter-1 f (- n 1) • (multiple-value-list (apply f args))))) • (defun Fib-step (a b) (values b (+ a b))) • (iter-1 'Fib-step 190 0 1) ; stack overflow ... • Iteration can be accelerated using function composition • (defun compose/2 (f g) • (lambda (&rest args) • (apply f (multiple-value-list (apply g args))))) • (defun compose (&rest functions) • (if (null functions) #'values • (compose/2 (car functions) • (apply 'compose (cdr functions)))))
Programming Example (cont. 2) • (defun iter-2 (f n &rest args) • (apply (apply #'iter-1 f (- n 1) • (multiple-value-list (apply f args))))) • (defun Fib-step (a b) (values b (+ a b))) • (iter-2 'Fib-step 190 0 1) ; stack overflow ... • Iteration can be accelerated using function composition • (defun compose/2 (f g) • (lambda (&rest args) • (apply f (multiple-value-list (apply g args))))) • (defun compose (&rest functions) • (if (null functions) #'values • (compose/2 (car functions) • (apply 'compose (cdr functions))))) • (defun iter-2 (f n &rest args) • (apply (apply 'compose (loop for i below n collect f)) • args)) • (iter-2 'Fib-step 103 0 1) ; stack overflow ...
Programming Example (cont. 3) • Remember tricky exponentiation: • (defun nat-expt (base exponent &optional (factor 1)) • (if (zerop exponent) factor • (nat-expt (* base base) • (ash exponent -1) • (if (oddp exponent) (* base factor) factor)))) • (defun fn-expt (fn iterations &optional (seed #'values)) • (if (zerop iterations) seed • (fn-expt (compose/2 fn fn) • (ash iterations -1) • (if (oddp iterations) (compose fn seed) seed) • ) ) ) • (defun iter-3 (f n &rest args) • (apply (fun-expt f n) args)) • (iter-3 'fib-step 100000 0 1) ; calculated in 3.5 s ???
Programming Example (cont. 4) • (defun generic-expt (composer function iterations seed) • (if (zerop iterations) seed • (generic-expt composer • (funcall composer function function) • (ash iterations -1) • (if (oddp iterations) • (funcall composer function seed) • seed)))) • (defun matrix-expt (m n &optional • (seed (identity-matrix (matrix-columns m)))) • (generic-expt 'matrix-compose m n seed)) • (defun fast-fib (n) • (matrix-element (matrix-expt '((0 1) (1 1)) n) 0 1)) • (fast-fib 100000) ; calculated in 0.343 s • (very-fast-fib 100000) ; calculated in 0.156 s
Programming Example (cont. 5) • (defun very-fast-fib (n) • (declare (optimize (speed 3) (safety 0) (debug 0))) • (check-type n fixnum) • (let ((a 0) (b 1) ;;; the matrix to exponentiate • (p 0) (q 1)) ;;; the seed vector to which to apply it • (loop • for c = (+ a b) • until (zerop n) • when (oddp n) • do (psetq p (+ (* a p) (* b q)) • q (+ (* b p) (* c q))) ;;; apply current mtrx • do (psetq n (ash n -1) ;;; halving the exponent • a (+ (* a a) (* b b)) • b (* b (+ a c))) ;;; squaring current matrix • finally (return p))))
Local vs. Global Variables ... • A simplified definition: • extent– the interval of time during which a reference to a variable is defined (can be dynamic or indefinite) • scope– the textual region of code in which reference to a variablecan occur (can be lexical or indefinite) • (defvar X) ; creates a dynamic (global) variable X • (defun FF (X) ...) ; creates a local variable X • Lisp uses lexical scoping as a rule, exceptions need an explicit specialdeclaration (defvar makes specialvars).
Example – A Simple Counter ... • (defvar *counter* 0) ; a global (dynamic) variable • (defun increment-c () (incf *counter* )) ; ??? • (defun reset-c () (setf *counter* 0)) • (defun counter-value () *counter*) • (let ((counter 0)) ; local counter introduced • (defun incr-let () (incf counter )) • (defun reset-let () (setf counter 0)) • (defun c-val-let () counter) • ) closure
Local vs. Global Variables ... • General form of let: • (let ((var-1 init-1)(var-2 init-2) ... (var-n init-n)) • exp-1 ... exp-k) • if no initial value specified,NIL is used, expk is the resulting value. • (let* ...)is similar to let, but initializations are performed in sequence • Modification of (variable) values: • (setfplace-1 new-value-1 • place-2 new-value-2 • ... • place-N new-value-N)
Generalized Variables • What are the places to be setf-ed ?? • (defvar X '(1 2 3 4 5 6)) ; X • (car X) ; 1 • (setf (car X) 0) ; 0 • X ; (0 2 3 4 5 6) • (nth 3 X) ; 4 • (setf (nth 3 X) 14) ; 14 • X ; (0 2 3 14 5 6) • (cdr X) ; (2 3 14 5 6) • (setf (cdr X) '(YA BASTA)) ; (YA BASTA) • X ; (0 YA BASTA) • Possibilities for place specification (generalized variables) • nth, first, second, ..., tenth, car, cdr, caar, ..., cddddr, • get, getf • and some more ...
Value-Changing Functions • Original modifiers • (rplaca x y ) (rplacd x y ) • (defvar X '(A B)) ; X • (defvar Y '(C)) ; Y • X ; (A B) • (rplaca X Y) ; ((C) B) • (setf X '(A B)) ; (A B) • (rplacd X Y) ; (A C) • (rplaca X X) ; ??? • (rplacd Y Y) ; ??? • More modifiers • (incf x [incr] ) (decf x [decr] )
Defining "setf" functions • We can extend functions that create generalized variables. Assume we want last to be used in setf with the effect of changing the last list element. • (defun (setf last( (val lst) • (setf (car (last lst)) val) • ) • > (defvar X '(A B)) • (A B) • > (setf (last X) 'C) • (A C) • General format • (defun (setf fun) (newval arg1 arg2 ...) body) • (setf (fun arg1 arg2 ...) val)