CSCI 275

Lab 04: Higher Order Functions – Fold, Map and Apply
Due Monday, March 23 at 11:59 PM

 

Part 1 -- Fold

In each of the following you can use either of the folds we have discussed: foldl or foldr

  1. Use fold to write (index a lat), which returns the 0-based index of atom a in lat, or -1 if a is not an element of lat. Examples:

o   (index 3 ‘(2 4 3 1 2 3)) returns 2

o   (index 3 ‘(2 4 6 4 2)) returns -1

  1. Use fold to write (replace a b lat), which replaces every instance of atom a with atom b. Examples:

o   (replace 3 5 '(1 2 3 4 5 4 3 2 1)) returns (1 2 5 4 5 4 5 2 1)

o   (replace 3 5 ‘( 2 4 6 8))  returns (2 4 6 8)

  1. Assume bags is globally defined to be an association list containing sublists of two elements. Each sublist represents the name of a piece of luggage and its respective weight, where the car is the name of the piece of luggage and the cadr is the weight.  For example, variable myBags might hold the list
           '( (duffle 8) (garment-bag 2) (briefcase 5) (valise 7) (steamer-trunk 65) )
    1. Use fold to write a procedure (weigh bags) which takes a baggage list and returns the total weight. For the baggage list shown above (weigh myBags)  should return 87.
    2. Use fold to write a procedure (heaviest bags) which returns the name of the bag with maximum weight. Assume that no weights are negative. For the baggage list above (heaviest myBags) should return 'steamer-trunk.

Part 2 -- Trees

For the rest of this assignment you will write a number of procedures that involve trees. Recall that a tree is a data structure that is either empty, or contains some data and has a list of children, each of which is a tree.

We will use Scheme procedures to implement the data type. At the beginning of the following section the tree datatype will be defined. All of this code is contained in the file "TreeDatatype.rkt".   Your solution file should start with the lines

#lang racket
(require "TreeDatatype.rkt")

You will need to download the file TreeDatatype.rkt Please include this file in your handin folder.

Introduction -- the tree structure

A tree is either

  1. an empty tree, which has no fields. We will represent this by the empty list.
  2. non-empty tree, which has a value field containing a number and a list-of-children field containing a list of children of the node, which are themselves trees.  We will represent this by a list whose first element is the atom ‘tree (so we can  distinguish between a list that represents a tree and any other list,  whose second element is the value, and whose third element is the list of children.

This leads immediately to the following functions.  All of the code in boldface is in the TreeDatatype.rkt file:

(define tree? (lambda (t) (or (empty-tree? t) (non-empty-tree? t))))

(define empty-tree? (lambda (x) (null? x)))

(define non-empty-tree? (lambda (x)
             (cond
                       [(list? x) (eq? (car x) 'tree)]
                       [else #f])))

; constructors

(define non-empty-tree (lambda (value list-of-children)
             (list 'tree value list-of-children)))

(define empty-tree (lambda () null))

When actually constructing trees, the non-empty-tree constructor requires us to make an explicit list of children.  For example, if we have already made trees t1, t2, and t3 and we want to make a new tree with value 0 and those three previously-made trees as its children we would need to say (non-empty-tree 0  (list t1 t2 t3)).  The following alternative constructor allows us to say (make-tree 0 t1 t2 t3).  Be careful using this in your recursions.  When recursing you probably already have the children as a list, so you will use the non-empty-tree constructor.

; Convenience constructor 

(define makeTree (lambda l 
              (non-empty-tree (car l) (cdr l))))

; utility functions

(define value (lambda (tree)
             (cond
                       [(empty-tree? tree) 'error]
                       [else (cadr tree)])))

(define list-of-children (lambda (tree)
                (cond
                       [(empty-tree? tree) 'error]
                       [else (caddr tree)])))

(define number-of-children (lambda (tr)
             (cond
                       [(empty-tree? tree) 'error]
                       [else (length (list-of-children tr))])))

(define leaf? (lambda (tr)
             (cond
                       [(empty-tree? tree) 'error]
                       [else (zero? (number-of-children tr))]))) 

; example trees

(define Empty (empty-tree))
(define T1 (makeTree 50)) 
(define T2 (makeTree 22)) 
(define T3 (makeTree 10)) 
(define T4 (makeTree 5)) 
(define T5 (makeTree 17)) 
(define T6 (makeTree 73 T1 T2 T3)) 
(define T7 (makeTree 100 T4 T5)) 
(define T8 (makeTree 16 T6 T7)) 

 

For example, here is a picture of T8 showing the others as subtrees:

 

 

Trees T1 through T8 and all of the code above is in file TreeDatatype.rkt

 

Tree Exercises

In the exercises that follow, you should use higher-order functions like map, apply, and fold to make your solution as concise as possible. You should also use letrec to define any recursive help functions.

All programs should be written using the tree operators defined above. Don't directly manipulate the list representations; use the tree procedures.

As usual, if the solution to an earlier problem is useful in a later one, go ahead and use it.

In all of these tr is a value of the tree datatype

  1. (childSum tr) This returns the sum of the values in the children of the given node. The tree is assumed to be non-empty. 

o   (childSum T8) returns 173

o   (childSum T6) returns 82.

  1. (allSum tr) This sums the entire set of values found in the tree. If tree is empty it returns 0. 

o   (allSum T8) returns; 293 

o   (allSum T6) returns 155

  1. (visitTree f tr) Here f is a function of 1 numerical variable. This returns a new tree with the same structure only every value v in tree has been transformed to (f v). For example, (visitTree add1  t)  increments every value in tree t by 1.
  2. (sizeof tr) This returns the number of nodes in the subtree rooted at the given node. 

o   (sizeof T8) returns 8

o   (sizeof T6) returns 4 

  1. (height tr) Returns the height of the tree -- the maxiumum number of edges from root to leaf.. Assume that the empty tree has height -1, a single-node tree (a leaf) has height 0, etc. 

o   (height T1) returns 0

o   (height T8) returns 2

  1. (count n tr) Returns the number of times value n appears in the tree. 

o   (count 22 T8) returns 1

o   (count 7 T8) returns 0 

Each of the next two procedures produces a flat list of the values in the tree. They differ only in the order in which the values are listed:

  1. (preorder tr) lists the value at the root ahead of the values of the children. 

o   (preorder T8) returns (16 73 50 22 10 100 5 17)

  1. (postorder tr) lists the value at the root after the values of the children. 

o   (postorder T8) returns (50 22 10 73 5 17 100 16)