#| class7.lsp Iterative Programming in LispStat - Comparison of iterative and recursive programs for x! - Bakquote ` and macros (while) - Non-iterative alternatives - Indexing - Comparisons |# ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Recursive factorial function (note the name !) ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun ! (k) "Args (k): returns k factorial" (if (= k 0) 1 (* k (! (- k 1))) ; Is this version tail recursive? )) (! 20) (defun ! (k &optional (soFar 1)) "Args (k): returns k factorial" (if (= k 0) soFar (! (- k 1) (* k soFar)) )) (compile '!) (! 20) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Iterative factorial using the general 'do' form ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #| ; --- syntax resembles the 'let' form (do ( (x x_0 x_1) (y y_0 y_1) ... ) ( termination condition terminal value) ( other forms ) ) |# (defun FACT (k) "Args (k): returns k factorial" ; Warning... buggy version! (do ((i k (- i 1)) (f k (* f i)) ) ( (= i 1) f ) )) (fact 20) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Iterative factorial using 'dotimes' ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun FACT (k) "Args (k): returns k factorial" (let ((f 1) ) (dotimes (i (- k 1)) ; Zero based counter! (setf f (* f (+ i 2)))) f)) (fact 6) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Backquote substitution mechanism ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;--- backquote is a general substitution method (setf expr '(+ 2 3)) `(setf x ,expr) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Macros provide different styles of iteration ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;--- Macros substitute things in a template, and then are evaluated. (defmacro print-expr (expr) `(format t "~a~%" ,expr) ) (print-expr (+ 2 3 4 5)) ;--- macro for iteration (defmacro WHILE (test &body body) `(do () ((not ,test)) ,@body)) ;--- Factorial using While construct (defun fact (k) (let ((i 1) (f 1)) (while (<= i k) (setf f (* f i)) (incf i) ) f)) (fact 3) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Avoiding iteration: built-in, accumulators ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;--- Happen to have the log of the gamma function built in! ; (really more useful for non-integers) (exp (log-gamma 4)) ; G(n) = (n-1)! ;--- Accumulate requires building a list which is not used ("garbage") (defun FACT (k) (cond ((= k 0) 1) ((= k 1) 1) ( t (first (last (accumulate #'* (iseq 2 k))))) )) (fact 3) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Iteration often requires indexing. ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (def x (iseq 20)) ;--- select a scalar member (select x 3) ;--- automatic bounds checking (select x 21) (select x 20) (select x 0) ;--- lists of indices (def in '(1 1 1 2 2 3)) (select x in) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Comparisons (summing list of squares) ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;--- Algorithms often appear simplest when use built-in functions ; at the expense of efficiency. (could use inner-product) ; If the list is built, use built-in functions. If you need ; to build it just to use them, it often is ineffective. (defun sum1 (a b) (sum (^ (iseq a b) 2))) (time (sum1 100 2000)) ;--- recursion often is simpler to write, and can be fast if ; formed as a tail recursion. (defun sum2 (a b &optional (sum 0)) (if (= a b) (+ (* a a) sum) (sum2 (1+ a) b (+ (* a a) sum)) )) (compile 'sum2) (time (sum2 100 2000)) ;--- iteration is often more "familiar" (defun sum3 (a b) (let ((sum 0) ) (dotimes (i (- b a)) (incf sum (^ (+ a i) 2)) ) sum)) (time (sum3 100 2000)) ;--- compiing helps iteration also (compile 'sum3) (time (sum3 100 2000))