130 likes | 280 Views
Iteration. “If I had a nickel for every time I've written ‘for (i = 0; i < N; i++)’ in C I'd be a millionaire.” - Mike Vanier. Do. The most basic iterative construct in LISP (do ([(var <init <incr>>)|var]*) (test result*) S-expression*)
E N D
Iteration “If I had a nickel for every time I've written ‘for (i = 0; i < N; i++)’ in C I'd be a millionaire.” - Mike Vanier
Do • The most basic iterative construct in LISP (do ([(var <init <incr>>)|var]*) (test result*) S-expression*) • “Initialize each var with its init, and execute the S-expressions until the test is true, modifying each var according to its incr. When test is true, return result” • Think of as do-until
Iteration Clause (do ([(var <init <incr>>)|var]*) (test result*) S-expression*) • Required, but can be empty • Introduces local variables, optionally gives them initial values, and optionally tells how to modify them with each iteration • Example: (x (y) (z 20) (a 1 (* a 2))) • x is set to nil by default, and is not modified every iteration • y is set to nil by default, and is not modified every iteration • z is set to 20, but is not modified every iteration • a is set to 1, and is multiplied by 2 every iteration
Test Clause (do ([(var <init <incr>>)|var]*) (test result*) S-expression*) • Required to be a non-empty list • Loop terminates when test evaluates to non-nil • Evaluates all of the result expressions and returns the value of the last
Do Example >(defun factorial (value) (do ((count 1 (1+ count)) (result 1)) ((> count value) result) (setf result (* count result)))) FACTORIAL >(factorial 3) 6
Order of Operations >(let ((x 'a)) (do ((x 1 (+ x 1)) (y x x) result) ((> x 5) result) (setf result (cons (list x y) result)))) ((5 4) (4 3) (3 2) (2 1) (1 A))
Iteration vs. Recursion • Iteration and recursion are capable of accomplishing the same things • Neither is more powerful than the other, but each has its advantages and disadvantages • In general, tail recursion is practically equivalent to iteration, and LISP actually optimizes tail recursive functions into iterative loops • With recursion, stack memory can be an issue • Choose the most natural and elegant solution to the problem
Do* • Bindings and incrementing in do occur in parallel • do* is the same as do, except the bindings in the iteration clause happen sequentially: >(do* ((a 0 (1+ a)) (b (+ 5 a) (* a b))) ((> a 5) b)) 3600
Dolist • Easy way to iterate over a list (dolist (element list result) S-expression*) • Iterate over each element of the list, execute the loop body, and at the end return the result >(let (result) (dolist (x '(a b c d) result) (setf result (cons x result)))) (D C B A)
Dotimes • Iterate over integers from 0 to n-1 (dotimes (int n result) S-expression*) • Example >(let (result) (dotimes (x 5 (reverse result)) (setf result (cons (list x (* x x)) result)))) ((0 0) (1 1) (2 4) (3 9) (4 16))
Terminating Early with Return • We can terminate early from iteration early using return, just like in typical imperative programming (somewhat like break as well) >(defun first-neg (list) (dolist (x list 'no-negatives) (if (minusp x) (return x)))) FIRST-NEG >(first-neg '(2 3 4 5 1 -3 -5 2 6 -2 3)) -3
Loop • loop is a complicated but powerful macro • The number of special directives available to loop is like a programming language in itself • We won’t go into too much detail on loop, but we’ll look at some examples • These examples use print, a simple output function that we’ll learn more about next class
>(loop for i in '(2 4 78) do (print i)) 2 4 78 NIL >(loop for i on '(2 4 78) do (print i)) (2 4 78) (4 78) (78) NIL >(loop for i from 1 to 3 do (print i)) 1 2 3 NIL >(loop with a = '(1 2 3) for i in a do (print i)) 1 2 3 NIL >(loop with result = '() for i from 5 downto 1 do (setf result (cons i result)) (when (= i 1) (return result))) (1 2 3 4 5) >(loop for i from 1 to 5 collect i) (1 2 3 4 5) Loop Examples