\input 6001mac
%\input psfig
\addtolength{\textheight}{1cm}
%\addtolength{\topmargin}{-1cm}

\newcommand{\comment}[1]{}

\comment{
\renewenvironment{lisp}{%
  \par
  \begin{minipage}[t]{\linewidth}
  \begin{list}{$\bullet$}{%
    \setlength{\topsep}{0in}
    \setlength{\partopsep}{0in}
    \setlength{\itemsep}{0in}
    \setlength{\parsep}{0in}
    \setlength{\leftmargin}{1.5em}
    \setlength{\rightmargin}{0in}
    \setlength{\itemindent}{0in}
  }\item[]
  \obeyspaces
  \obeylines \small\tt}{%
  \end{list}
  \end{minipage}
  \par
  }
}


\begin{document}

\vspace*{-0.5in}\psetheader{Fall Semester, 1996}
{Lecture Notes, November 7 -- Infinite Streams}

The stream data abstraction:

\framebox[6.5in]{\rule{0in}{1.5in}}

% \subsubsection{A Simple Stream}
\begin{lisp}
(define (integers-from n)
  (cons-stream n
               (integers-from (+ 1 n))))
\null
(define integers (integers-from 1))
\end{lisp}

\subsubsection{Implementation Strategies}
\begin{itemize}
  \item Use lazy evaluator declarations
 \\ \framebox[6.5in]{\rule{0in}{1in}}
  \item Extend eval with delays as ``thunks'' or ``promises''
 \\ \framebox[6.5in]{\rule{0in}{1in}}
  \item Extend eval with delays as procedures (or
   memoized procedures)
 \\ \framebox[6.5in]{\rule{0in}{1in}}
\end{itemize}

\subsubsection{Stream Higher Order Procedures}
\begin{lisp}
(define (stream-ref s n)
  (if (= n 0)
      (stream-car s)
    (stream-ref (stream-cdr s) (- n 1))))
\null
(define (stream-map proc stream)
  (if (stream-null? stream)
      the-empty-stream
      (cons-stream (proc (stream-car stream))
                   (stream-map proc (stream-cdr stream)))))
\null
(define (stream-filter pred s)
  (cond ((stream-null? s) the-empty-stream)
        ((pred (stream-car s))
         (cons-stream (stream-car s)
                      (stream-filter pred (stream-cdr s))))
        (else (stream-filter pred (stream-cdr s)))))
\null
(define (enumerate-interval low high)
  (if (> low high)
      the-empty-stream
      (cons-stream low 
                   (enumerate-interval (+ low 1) high))))
\end{lisp}


\subsubsection{Examples}
\begin{lisp}
(define no-sevens
  (stream-filter (lambda (n) (not (divisible? n 7))) 
                 integers))
\null
(define (divisible? x y) (= (remainder x y) 0))
\null
(stream-ref no-sevens 100)
; Value: 117
\end{lisp}

\newpage
\subsubsection{The Sieve of Erasthosthenes - Prime Number Generation}
\begin{lisp}
(define (sieve s)
  (cons-stream (stream-car s)
               (sieve (stream-filter 
                       (lambda (x) (not (divisible? x (stream-car s))))
                       (stream-cdr s)))))

(define primes (sieve (integers-from 2)))

(stream-ref primes 200)
\end{lisp}

\subsubsection{Other Stream Utilities}
\begin{lisp}
(define (show-stream s n)
  (cond ((= n 0) 'done)
        (else (write-line (stream-car s))
              (show-stream (stream-cdr s) (- n 1)))))
\null
(define (add-streams s1 s2)
  (cond ((stream-null? s1) s2)
        ((stream-null? s2) s1)
        (else
         (cons-stream (+ (stream-car s1) (stream-car s2))
                      (add-streams (stream-cdr s1) (stream-cdr s2))))))
\null
(define (scale-stream c s)
  (stream-map (lambda (x) (* x c)) s))
\null
(define (stream-map2 proc s1 s2)
  (if (stream-null? s1)
      the-empty-stream
      (cons-stream (proc (stream-car s1) (stream-car s2))
                   (stream-map2 proc (stream-cdr s1) (stream-cdr s2)))))
\end{lisp}

\subsubsection{Examples}
\begin{lisp}
(define ones (cons-stream 1 ones))
\null
(define integers (cons-stream 1 (add-streams ones integers)))
\end{lisp}

A worksheet or table for the stream:

\framebox[6.5in]{\rule{0in}{1in}}


\subsubsection{Stream of Fibonacci Numbers}
\begin{lisp}
(define fibs
  (cons-stream 0
               (cons-stream 1
                            (add-streams (stream-cdr fibs)
                                         fibs))))
\end{lisp}

A worksheet or table for the stream:

\framebox[6.5in]{\rule{0in}{1in}}

\subsubsection{Stream of Square Roots}
\begin{lisp}
(define (sqrt-improve guess x)
  (average guess (/ x guess)))
(define (average a b) (/ (+ a b) 2))
\null
(define (sqrt-stream x)
  (cons-stream 1.0
               (stream-map (lambda (g)
                             (sqrt-improve g x))
                           (sqrt-stream x))))
\null
;; Follow the stream until desired tolerance is reached
(define (stream-limit s tol)
  (define (iter s)
    (let ((f1 (stream-car s))
          (f2 (stream-car (stream-cdr s))))
      (if (close-enuf? f1 f2 tol)
          f2
          (iter (stream-cdr s)))))
  (iter s))
\null
(define (close-enuf? x y tol)
  (< (abs (- x y)) tol))
\null
(stream-limit (sqrt-stream 2) 1.e-5) 
\end{lisp}

\subsubsection{Trapezoidal Integration}
\begin{lisp}
(define (trapezoid f a b h)
  (let ((dx (* (- b a) h))
        (n (/ 1 h)))
    (define (iter i sum)
      (let ((x (+ a (* i dx))))
        (if (>= i n)
            sum
            (iter (+ i 1) (+ sum (f x))))))
    (* dx (iter 1 (+ (/ (f a) 2) (/ (f b) 2))))))
\end{lisp}

\subsubsection{The Witch of Agnesi and Approximations to $\pi$}
\begin{lisp}
(define (witch x)
  (/ 4 (+ 1 (* x x))))
\null
(trapezoid witch 0. 1. .1)
;Value: 3.1399259889071587
\null
(trapezoid witch 0. 1. .01)
;Value: 3.141575986923129
\end{lisp}

\medskip
To learn more about Maria Agnesi, see
{\tt http://www.agnesscott.edu/lriddle/women/agnesi.htm}.

\subsubsection{Stream of Approximations to $\pi$}
\begin{lisp}
(define (keep-halving R h)
  (cons-stream (R h)
               (keep-halving R (/ h 2))))
\null
(show-stream (keep-halving (lambda (h) (trapezoid witch 0 1 h)) 0.1)
             10)
\null
(stream-limit
 (keep-halving (lambda (h) (trapezoid witch 0 1 h)) 0.5)
 1.e-9)
\end{lisp}


\newpage
\subsubsection{Accelerating the Approximation}
Suppose we want to approximate a function $R(0)$ and we have
the sequence of values $R(h), R(h/2), R(h/4), \cdots$.
Suppose we also know that $R$ has the form $R(h) = A + Bh^p + Ch^{2p} + Dh^{3p} + \cdots$.
Then
\begin{equation}
  \frac{2^pR(h/2) - R(h)}{2^p-1} = A + C_2h^{2p} + D_2h^{3p} + \cdots
\end{equation}
That is to say, this new sequence converges to the same value as the
original, but it converges faster.  We can formulate this as a stream
process:

\begin{lisp}
(define (accel-halving-seq s p)
  (let ((2**p (expt 2 p)))
    (let ((2**p-1 (- 2**p 1)))
      (stream-map2 (lambda (Rh Rh/2)
                     (/ (- (* 2**p Rh/2)
                           Rh)
                        2**p-1))
                   s
                   (stream-cdr s)))))
\end{lisp}

Make a table, where each row is the accelerated version of the previous row.
\begin{lisp}
(define (make-tableau s p)
  (define (rows s order)
    (cons-stream s
                 (rows (accel-halving-seq s order)
                       (+ order p))))
  (rows s p))
\end{lisp}

Finally, take just the first element of each row to get the
``Richardson acceleration'' of the original series:
\begin{lisp}
(define (richardson-accel s p)
  (stream-map stream-car
              (make-tableau s p)))
\null
(show-stream  (richardson-accel
               (keep-halving (lambda (h) (trapezoid witch 0 1 h)) .1)
               2)
              6)
\null
(stream-limit (richardson-accel
               (keep-halving (lambda (h) (trapezoid witch 0 1 h)) .1)
               2)
              1.e-9)
\null
; Value: 3.1415926536207945
\end{lisp}
This requires only 73 evaluations of the witch to get $\pi$ to 
9 decimal places!


\end{document}
