;;; File:
;;;   watch-recursion.ss
;;; Author:
;;;   Samuel A. Rebelsky
;;; Version:
;;;   1.0 of September 2003
;;; Summary:
;;;   Some extended recursive procedures that let us "watch" the 
;;;   steps in recursive evaluation.

;;; Name:
;;;   watch
;;; Type:
;;;   boolean
;;; Value:
;;;   Set to #t if we want to "watch" procedures and false otherwise.
(define watch #t)

;;; Procedure:
;;;   observe
;;; Parameters:
;;;   procName, a value
;;;   params, a list of values
;;; Purpose:
;;;   Observe that the procedure has been colled.
;;; Produces:
;;;   Nothing; called for side-effects.
;;; Preconditions:
;;;   watch is defined.
;;; Postconditions:
;;;   Output may have been printed.
(define observe
  (lambda (proc params)
    (display (cons proc params))
    (newline)))
  
;;; Procedures:
;;;   sum
;;;   new-sum
;;;   newer-sum
;;; Parameters:
;;;   numbers, a list of numbers.
;;; Purpose:
;;;   Find the sum of the elements of a given list of numbers
;;; Produces:
;;;   total, a number.
;;; Preconditions:
;;;   All the elements of numbers must be numbers.
;;; Postcondition:
;;;   total is the result of adding together all of the elements of ls.
;;;   total is a number.
;;;   If all the values in numbers are exact, total is exact.
;;;   If any values in numbers are inexact, total is inexact.

; Most popular: Clinton
;   Straightforward
;   Doesn't need an extra procedure
;   Who cares about tradition?  sum is commutative
(define sum
  (lambda (numbers)
    (display (list 'sum numbers))
    (newline)
    (if (null? numbers)
        0
        (+ (car numbers) (sum (cdr numbers))))))

; Moderately popular: All eligible democratic candidates
;   Sums in the traditional way (left-to-right rather than right-to-left)
;   Probably more efficient.
;   Easier to "observe"
(define new-sum
  (lambda (numbers)
    (new-sum-helper 0 numbers)))

; Unpopular: Fred the Dog
(define newer-sum
  (lambda (numbers)
    (new-sum-helper (car numbers) (cdr numbers))))

;;; Procedure:
;;;   new-sum-helper
;;; Parameters:
;;;   sum-so-far, a number.
;;;   remaining, a list of numbers.
;;; Purpose:
;;;   Add sum-so-far to the sum of the elements of a given list of numbers
;;; Produces:
;;;   total, a number.
;;; Preconditions:
;;;   All the elements of remaining must be numbers.
;;;   sum-so-far must be a number.
;;; Postcondition:
;;;   total is the result of adding together sum-so-far and all of the 
;;;     elements of remaining.
;;;   total is a number.
;;;   If both sum-so-far and all the values in remaining are exact, 
;;;     total is exact.
;;;   If either sum-so-far or any values in remaining are inexact, 
;;;     total is inexact.
(define new-sum-helper
  (lambda (sum-so-far remaining)
    (display (list 'new-sum-helper sum-so-far remaining))
    (newline)
    (if (null? remaining)
        sum-so-far
        (new-sum-helper (+ sum-so-far (car remaining))
                        (cdr remaining)))))



;;; Procedure:
;;;   step
;;; Parameters:
;;;   prefix, a string
;;;   value, a Scheme value
;;;   suffix, a string
;;; Purpose:
;;;   Displays prefix, value, and suffix on a line.
;;;   Used primarily for showing steps of computation.
;;; Produces:
;;;   value (exactly the same as value).
;;; Preconditions:
;;;   [Standard]
;;; Postconditions:
;;;   The parameters have been displayed.
(define step
  (lambda (prefix value suffix)
    (display prefix)
    (display value)
    (display suffix)
    (newline)
    value))

;;; Procedure:
;;;   watch-sum
;;; Parameters:
;;;   lst, a list of numbers of the form (n_1 n_2 ... n_k)
;;; Purposes:
;;;   Sum the values in lst.
;;;   Show the steps involved in computing that sum.
;;; Produces:
;;;   result, a number
;;; Preconditions:
;;;   [Standard]
;;; Postconditions:
;;;   result = n_1 + n_2 + ... + n_k.
;;;   The steps in evaluation have been printed.
(define watch-sum
  (lambda (lst)
    (watch-sum-helper lst "" "")))

(define watch-sum-helper
  (lambda (lst prefix suffix)
    (step prefix (list 'sum (cons 'list lst)) suffix)
    (if (null? lst)
        (step prefix 0 suffix)
        (step prefix
              (+ (car lst)
                 (watch-sum-helper (cdr lst)
                                   (string-append prefix 
                                                  "(+ "
                                                  (number->string (car lst))
                                                  " ")
                                   (string-append ")" suffix)))
              suffix))))

;;; Procedure:
;;;   watch-greatest-of-list
;;; Parameters:
;;;   numbers, a list of numbers of the form (n_1 n_2 ... n_k)
;;; Purposes:
;;;   Find the largest value in numbers.
;;;   Show the steps involved in computing that value.
;;; Produces:
;;;   greatest, a number
;;; Preconditions:
;;;   [Standard]
;;; Postconditions:
;;;    greatest >= n_i for all reasonable i.
(define watch-greatest-of-list 
  (lambda (numbers)
    (wgol-helper numbers "" "")))

(define wgol-helper
  (lambda (numbers prefix suffix)
    (step prefix (list 'greatest-of-list (cons 'list numbers)) suffix)
    (if (null? (cdr numbers))
        (step prefix (car numbers) suffix)
        (step prefix
              (max (car numbers)
                   (wgol-helper (cdr numbers)
                                (string-append prefix
                                               "(max " 
                                               (number->string (car numbers))
                                               " ")
                                (string-append ")" suffix)))
              suffix))))
