Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

Code Block
;; foldr: (lambda any1 any2 -> any2) any2 list-of-any1 --> any2
(define (foldr func base loa)
   (cond
      [(empty? loa) base]
      [(cons? loa) (func (first loa)(foldr func base_value (rest loa))])) 

 The template is gone!   The code is now 100% concrete, with nothing left to fill in!   We have defined a function, called "foldr" ("fold-right"), which the same foldr defined in the last lab:

...

Let's look at how we would sum a list of numbers using "forward Forward accumulation":

Code Block
(define (sum_fwd lon)
  (cond
    [(empty? lon) 0]
    [(cons? lon)
     (local
       [(define (helper acc aLon)
          (cond
            [(empty? aLon) acc]
            [(cons? aLon) (helper (+ (first aLon) acc) (rest aLon))]))]
       (helper (first lon) (rest lon)))]))   

Notice, first of all, that the function requires a helper function? Why?

Because the only way to pass data forward in the list is to use an input parameter, but since forward accumulation is an implementation detail of the function, there are no (and should not be any) provisions in the input parameters of the original function, sum_fwd, for passing the accumulated result. Thus a helper function with an extra input parameter is needed, to handle the accumulating value.

...

  1. The "..."'s in both the helper and the outer cons? clause must be identical.
  2. "base" must represent a value, as opposed to something such as a exception.

For instance, the finding the largest element in a list follows only the first template:

It is important to emphasize again that this is a special case where a base value is well-defined.   The general case template above will always work and should be the template of choice if you are not sure how to write your forward accumulation algorithm.   Once you get it working, you can convert it to the more specialized case here if it warrants it.  

For instance, the finding the largest element in a list follows only the general template:

Code Block

(define (get
Code Block

(define (get_largest lon)
  (cond
    [(empty? lon) (error "no largest in an empty list")]
    [(cons? lon)
     (local
       [(define (helper acc lon2)
          (cond
            [(empty? lon2) acc]
            [(cons? lon2) (helper (if (> (first lon2) acc) (first lon2) acc) (rest lon2))]))]
       (helper (first lon) (rest lon)))]))   

...

Code Block
;; make-ints: int -> list-of-ints
;; makes a list of ascending integers from 1 to n or empty if n = 0.
;; Examples:
(check-expect (make-ints 0) empty)
(check-expect (make-ints 1) (list 1))
(check-expect (make-ints 2) (list 1 2))
(check-expect (make-ints 3) (list 1 2 3))

(define (make-ints n)
  (cond
    [(zero? n) empty]
    [(positive? n)
     (local
       [(define (helper acc i)
          (cond
            [(zero? i) acc]
            [(positive? i) (helper (cons i acc) (sub1 i))]))]
       (helper (list n) (sub1 n)))]))


(define ints (make-ints 1000000))  ;; reduce this value if you run out of memory intially.

;;max: num num --> num
;; returns the larger of the two numbers.

;; The following functions both return the largest value in the given list of positive numbers
(time (foldr max 0 ints))  ;; try increasing the size of ints above by factors of 10 until you run out of memory.
(time (foldl max 0 ints))  ;; comment out the foldr line and see if you still run out of memory. 

You should see that the foldl version runs significantly faster than the foldr version. And if you comment out foldr and foldl one at a time, try increasing the size of "ints" by a factor of 10, you will see that foldl runs with much less memory than foldr. Why do you think that I wrote "make-ints" as a forward accumulation algorithm?

Delegation Model Programming

Loking beyond the bounds of functional programming, there is an important viewpoint on the accumulation process that becomes increasingly useful as the size of your programs scale upwards.  Consider this comparison between reverse and forward accumulation:

  • In reverse accumulation, you delegate to a function that processes the rest of the list.   That function then returns a value (the accumulator) that you then use to complete your processing of the current data (first) and thus create the new accumulator value, (the return value, which is the completed result).
  • In forward accumulation, you delegate to a function that processes the rest of the list.   That function takes the partially processed data (the new accumulator, formed from first and the previous accumulator value) and completes the processing, returning the completed result.

Both processes can be expressed in terms of a delegation to the rest of the list.   The only difference is what the function on the rest of list is asked to do.  

In "delegation model programming", the notion is that the overall processing of data involves two main factors:  the local processing of local data, e.g. the first of a list, and the delegated processing of non-local data, e.g. the rest of the list.   Processing never crosses encapsulation barriers, so to the processing of encapsulated data, e.g. the data contained in the rest of the list, is always done via a delegation to another function.  

When we move to object-oriented programming, the delegation model viewpoint will become the major mode in which we break down the processing of data between objects, which are amongst other things, encapsulated data.  Specifically, the overall processing of a linked structure of objects will involve the delegation from one object to another.