Some midterm stuff

Many of you did poorly on the midterm problem that asked you to write
MY-SUBSTITUTE applicately.  Here's a solution and explanation:

   (defun my-substitute (new old list)
      (mapcar #'(lambda (x) (if (eql x old) new x)) list))

Recall that mapcar takes a function and applies it to each element in a
list, and it returns a list of those results.  For a simple example,

   (mapcar  #'(lambda (x) (+ x 1))  '(1 2 5 6))   -->   (2 3 6 7)

shows how to use MAPCAR to add 1 to all the elements of a list.
Pictorially, you can think of mapcar working like this:

   (mapcar f list)
   
   list =    (      1        2         5         6      )
      
                    |        |         |         |
                    |        |         |         |
                    v        v         v         v
      
                   f(x)     f(x)      f(x)      f(x)
      
                    |        |         |         |
                    |        |         |         |
                    v        v         v         v
      
   result =  (      2        3         6         7      )

In the case of MY-SUBSTITUTE, given that it works like

   (my-substitute 'f 'a '(a b a c))   -->   (F B F C)

the question simply becomes "what function do we need to push the list
down through to get the desired output?"

   list =    (      A        B         A         C      )

                    |        |         |         |
                    v        v         v         v

                    What function needs to go here?

                    |        |         |         |
                    v        v         v         v

   result =  (      F        B         F         C      )

The solution, again, is:

   (defun my-substitute (new old list)
      (mapcar #'(lambda (x) (if (eql x old) new x)) list))

The lambda function simply tests to see if an element should be
substituted--if so, it returns the new element, else the original
element unchanged.  Recall that these two

   (if x y z)                (cond (x y)
                                   (T z))

are equivalent.


Iteration

So far, we've seen two different ways of controlling program 
flow.  The first was recursion, and the second was what we 
called applicative programming.  They're both very 
functional.  But now that you know about variables and 
assignment, you're finally ready to learn about iteration.

Common LISP provides you with two very simple iterative 
forms.  The first is called "dotimes" and looks like this:

(dotimes ([variable] [limit-expr] [result-expr])
                         :
                         :
                    [body of code]
                         :
                         :
                                                   )

[variable] is a symbol.  You specify the name, and LISP 
initializes the variable to 0.  It is not evaluated.

[limit-expr] is an expression which is evaluated; LISP 
expects a number to be returned here.

[result-expr] is an optional argument.  When the iteration is 
finished, this expression is evaluated and the result is 
returned by "dotimes".  If no expression is found here, 
"dotimes" returns nil when it is done.

This algorithm describes the operation of "dotimes":

1.  [variable] := 0
2.  if [variable] = [limit-expr] 
    then return [result-expr] (or nil if no [result-expr])
3.  execute body
4.  [variable] := [variable] + 1
5.  go to 2

See, it's very simple.  Don't you wish I had let you use this 
earlier?  I don't.  Anyway, here's an example of using 
"dotimes" in, what else, a factorial function:

(defun factorial (number)
  (let ((result 1))
    (dotimes (counter number result)
      (setf result (* result (+ 1 counter))))))

The second simple iterative form is "dolist", which looks 
like this:

(dolist ([variable] [list-expr] [result-expr])
                         :
                         :
                    [body of code]
                         :
                         :
                                                   )

[variable] is a symbol.  You specify the name, and LISP binds 
this symbol to successive elements of the list given in 
[list-expr].  [variable] is not evaluated.

[list-expr] is an expression which is evaluated; LISP expects 
a list to be returned here.

[result-expr] is an optional argument.  When the iteration is 
finished, this expression is evaluated and the result is 
returned by "dolist".  If no expression is found here, 
"dolist" returns nil when it is done.

This algorithm describes the operation of "dolist":

1.  if there's no next element of [list-expr]
    then return [result-expr] (or nil if no [result-expr])
2.  [variable] := next element of [list-expr]
3.  execute body
4.  go to 1

Note that [list-expr] isn't changed while "dolist" is 
running---it doesn't "shrink" by one element each time the 
body of code is executed.  Here's an example of "dolist" in 
"my-remove":

(defun my-remove (item r-list)
  (let ((result nil))
    (dolist (element r-list result)
      (cond ((not (eql element item))
             (setf result (append result (list element))))))))

There's another iterative form which is not so simple, at 
least in my opinion.  It's just called "do", and it looks like this:

(do (([var-1] [init-expr-1] [update-expr-1])
     ([var-2] [init-expr-2] [update-expr-2])
                :
                :
     ([var-n] [init-expr-n] [update-expr-n]))
     ([test-expr] [action-expr-1] ... [action-expr-m])
                :
          [body of code]
                :
                             )

Here's how "do" works:

1.  each [var-i] is bound to its corresponding [init-expr-i]
    (in "parallel", just like with "let"---that is, there's
    no guarantee of the order the bindings are completed, so 
    don't encode dependencies of any kind in this part of the
    "do" form).
2.  [test-expr] is evaluated.  If the result is non-nil, then
    the [action-expr-1] through [action-expr-m] are evaluated
    in left-to-right fashion.  The "do" form returns the
    value of [action-expr-m].
3.  if evaluating [test-expr] returns nil, then execute the 
    body of code.
4.  when the body of code has been executed, update each
    [var-i] by binding it to the value obtained by evaluating
    the corresponding [update-expr-i].  The [update-expr-i]
    are optional, so if it's not there, then the various 
    [var-i] are never changed.  Also, these bindings are done
    automatically by "do", so there's no need to include
    your own "setf" or "setq".
5.  go to 2

If you use

(return)

or

(return [expr]) 

in the body of a "do" or "dotimes" or "dolist", the iterative 
form will be exited immediately, and will return nil or 
[expr] accordingly.


Weird science

With this powerful "do" form, you can write functions where 
all the work is done in the "preamble":

(defun factorial (n)
  (do ((count n (- count 1))
       (result 1 (* result count)))
      ((= 0 count) result)
  ;; no body!
  ))

Some authors of LISP texts (who shall remain nameless) think 
that this is elegant programming style.  Frankly, it makes my 
head hurt.  Use it if you want to, but please don't ask me
to debug it.


Other forms of iteration

Another iteration mechanism is the "loop" macro (we'll see 
more about macros soon).  It has been added to the Common 
LISP standard as described in the second edition of Steele, 
but it isn't in the first edition.  Some LISP folks complain
that the loop macro isn't defined all that well in the
second edition, which leads to some ambiguities.  Consequently,
we don't cover it in this class, but feel free to look it up.

And while you're thinking about things iterative, grab
a LISP book and look up the granddaddy of all LISP 
iterative forms: "prog".  Then once you've read about it, 
remember to try to avoid its use.  The inclusion of the 
"goto" in prog means you want to try to exclude it from 
your code in most cases.



Lecture notes by Kurt Eiselt, 1998.
Minor changes / additions by Brian McNamara, 1998.
Last updated on Thu Aug 13 02:21:37 EDT 1998 by Brian McNamara