20-CS-4003-001 Organization of Programming Languages Fall 2017

Lambda calculus, Type theory, Formal semantics, Program analysis

    Prev     Next     All lectures           Code

Find the maximum in a list

;; A stream of integers, not in any order.  The ith token
;; in the stream is always the same given the same n and m.
;; Example: (predictable-stream$ 7 67)
(define predictable-stream$
  (lambda (n m)
     (cons n 
       (lambda () 
         (predictable-stream$ (modulo (* n n) m) m)))))

;; Takes a stream S$ and integer n as input.  Produces a 
;; list of the first n tokens in S$.
;; Example: (take 20 (predictable-stream$ 7 67))
;; ;Value 11: (7 49 56 54 35 19 26 6 36 23 60 49 56 54 35 
;;            19 26 6 36 23)
(define take
   (lambda (n S$)
      (if (or (null? S$) (= n 0))
	  (if (and (not (null? (cdr S$)))
                   (procedure? (cdr S$)))
              (cons (car S$)
                    (take (- n 1) ((cdr S$))))
              (cons (car S$) 
                    (take (- n 1) (cdr S$)))))))

;; Takes a stream S$ and integer n as input.  Produces the 
;; stream that remains after the first n tokens are removed 
;; from S$.  (take$ 1 S$) has the same effect as ((cdr S$)), 
;; (take$ 2 S$) has the same effect as ((cdr ((cdr S$)))) 
;; and so on. 
;; Example: (take$ 10 (predictable-stream$ 7 67))
;; ;Value 12: (60 . #[compound-procedure 13])
(define take$
   (lambda (n S$)
      (if (null? S$) 
	  (if (or (= n 0) (null? (cdr S$)))
	      (take$ (- n 1) ((cdr S$)))))))

;; A simple approach to finding the maximum number in a 
;; list. The input is a list of numbers.  The output is 
;; the maximum number in the list (as a single element 
;; in a list).
;; Example: (fm aa) where aa =
;; (83177 98817 36877 78900 69309 80489 99273 38603 
;;  82739 20989)
;; ;Value 15: (99273)
(define fm
  (lambda (S)
    (if (null? S)
        (if (null? (cdr S))
	    (fm (cons (max (car S) (cadr S)) (cddr S)))))))

;; The usual changes to streams from lists are applied to
;; fm to get fm$ but this does not work because the call
;; to fm$ is not inside a thunk.
(define fm$
  (lambda (S$)
    (if (null? S$)
	(if (null? ((cdr S$)))
	    (fm$ (cons (max (car S$) (car ((cdr S$)))) 
		       (lambda () ((cdr ((cdr S$)))))))))))

;; This is an attempt at solving problem 2 but this does 
;; not work.
(define fm1$
  (lambda (S$)
    (if (null? S$)
	(if (null? (take$ 1 S$))
	    (cons (max (car S$) (car (take$ 1 S$))) 
		  (lambda () (fm1$ (take$ 1 S$))))))))

;; This works but is a little clumsey
;; Example: (take 16 (fm2$ -1 (predictable-stream$ 7 67)))
;; ;Value 22: (7 49 56 56 56 56 56 56 56 56 60 60 60
;;             60 60 60)
(define fm2$
  (lambda (high S$)
    (if (null? S$)
	(if (null? (take$ 1 S$))
	    (let ((n (max high (car S$))))
	      (cons n (lambda () 
                   (fm2$ n (take$ 1 S$)))))))))

;; A slight improvement in using the above
;; Example: (take 16 (fm3$ (predictable-stream$ 7 67)))
;; ;Value 44: (7 49 56 56 56 56 56 56 56 56 60 60 60 
;;             60 60 60)
(define fm3$ (lambda (S$) (fm2$ (car S$) S$)))

;; aa is used above in an example
(define ints
  (lambda (n)
    (if (= n 0)
        (cons (random 100000) (ints (- n 1))))))

(define aa (ints 10))
 -  The code to the left illustrates some practical problems in creating stream solutions based on list solutions.

Procedure fm takes a list of numbers as input and outputs the maximum number in the list, as a list of one element. The code recursively replaces the second number in the list with the maximum of the first two, dropping the first one. The maximum seen so far is then always the first number in the list. The list shrinks to one number when the process terminates.

Procedure fm$ has the usual changes in moving from lists to streams, namely (cdr s) is replaced by ((cdr s)) and (lambda () ...) surrounds new streams. This does not work, though, because fm$ calls itself and there is no (lambda () to stop its recursion. An additional problem is that the same token is demanded more than once - this would be fatal if the stream were generated using (random ...).

Procedure fm1$ is an attempt to fix the problem with fm$. The recursive call to fm1$ is inside a think but the second of the two arguments of max should not produce a token from S but a token from fm1$ applied to (take$ 1 S). However, if that is done, fm1$ will not be inside a think so problem 2 recurs.

Procedure fm2$ does work but is slightly clumsey since it requires two arguments. The idea is to pass the current maximum into the procedure along with the remainder of the stream begin checked. This avoids taking a token from the fm2$ result to make the comparison in max. Procedure fm3$ at least makes the use of fm2$ easier.