Ana Ramírez Chang
CS 302
Assignment 5

 

Problem Statement

Write a function treefold, similar to foldr that takes a combination function, a base value and a tree. Use the following datatype for a tree with an arbitrary branching factor.

	datatype 'a tree = Tree of 'a * 'a tree list

A tree consists of an element and a list of subtrees. For example, the value

    val tr: int tree = Tree(3, [Tree(2, [Tree(8, nil), 
                                         Tree(5, nil),
                                         Tree(7, [Tree(11, nil),
                                                  Tree(9, nil)])]),
                                Tree(6, nil)]);

represents the tree

     




Exercises
  1. What can you use foldr to do? List a few examples.
  2. What is the type of foldr?
  3. What can treefold be used for?
Preparation The reader should be able to use foldr, understand what a tree is and the datatype in the problem statement, undersand let expressions and understand curried functions.

Understanding treefold

What can you use foldr to do?

foldr abstracts out the recursion necessary to traverse a list. You can implement any recursive function over a list with foldr (as long as the order the list is recursed in doesn't matter or the order is from right to left.) Some examples include:

  • Summing all the numbers in an int list.
  • Reversing the elements of a list.
  • Adding 1 to each element of an int list.
What is the type of foldr?

foldr folds an 'a list into a value of type 'b. At each step, it combines the head element in the list with the result so far. So the type of the combination function is ('a * 'b -> 'b). foldr also needs a value of type 'b to combine with the first element in the list. So foldr takes a combination function of type ('a * 'b -> 'b), a base value of type 'b, and an 'a list and returns a value of type 'b.

foldr: ('a * 'b -> 'b) -> 'b -> 'a list -> 'b

Stop and Practice-> Implement a few functions using foldr.
What can treefold be used for?

Just like foldr, treefold also abstracts out recursion, but instead of abstracting out the recursion to recurse a list, it abstracts out the recursion to recurse a tree. You can implement any recursive function over a tree that recurses the tree in the same order as your treefold function. For example:

  • A function that flattens a tree into a list.
  • A function that sums all the elements of an int tree.
Stop and Predict -> What is the type of treefold.
What is the type of treefold? treefold: ('a * 'b -> 'b) -> 'b -> 'a tree -> 'b
Stop and Practice-> Implement a few functions using treefold.

Implementing treefold

How can we break the problem into smaller pieces? Since treefold abstracts out the recursion over a tree, we can break it down into the base case and the recursive case.
What will the skeletin of treefold look like?

For the skeletin, just lay out the base case and the recursive case without implementing eithr of them. This way you will have parameter variable names to refer to in your thinking about the solution.

	  fun treefold f base Tree(elt, nil) = ...
	  |   treefold f base Tree(elt, subTreeList) = ...
What does the base case look like? (pseudo code for now)

In the base case we don't have any more recursing to do, so we just need to apply the combination function to the elt and base.

	  fun treefold f base Tree(elt, nil) = apply f to base and elt
Stop and Participate -> Write the code for the base case.
What does the recursive case look like?

In the recursive case, we need to recurse over all the sub trees. So we need to apply the combination function to the current element and the result so far (base) and use the result as the new base as we recurse over the subtrees.

	  |   treefold f base Tree(elt, subTreeList) = 
	          apply f to base and elt and use the result as the new base 
	          as you fold all the subtrees together.
How can we fold the sub trees together? Our treefold function can fold a tree, but we need to fold a list of trees together.
How do we fold a list of trees together? We know how to fold a tree ad we have a list of trees, so we can use foldr to fold the sub trees together.
What arguments should we pass to foldr?

Lets look at the types. We have an 'a tree list and we want to get a value of type 'b. First, we should rewrite the type of foldr with different letters for the polymorphic types so we don't confuse them with the polymorphic types in treefold ('a and 'b).

foldr: ('m * 'n -> 'n) -> 'n -> 'm list -> 'n

Stop and Predict -> If we have an 'a tree list, what type will 'm be when we use foldr to fold our 'a tree list?
What is the type of foldr for this specific use?
  1. We will pass an 'a tree list into the argument of type 'm list, so 'm will be 'a tree.
  2. We want to end up with a value of type 'b, so 'n will be 'b.
    foldr: ('m      * 'n -> 'n) -> 'n -> 'm list      -> 'n
    foldr: ('a tree * 'b -> 'b) -> 'b -> 'a tree list -> 'b
Which arguments for foldr do we already have?

We already have the base case of type 'b (from applying f to elt and base).

We have an 'a tree list (subTreeList)

Which arguments do we still need? We need a function of type ('a tree * 'b -> 'b) that folds an 'a tree into a value of type 'b using the base valeu of type 'b.
How can we fold a tree?

Well, treefold will fold a tree, we just need to lign up the types.

treefold: ('a * 'b -> 'b) -> 'b -> 'a tree -> 'b

Which arguments do we have? f has type ('a * 'b -> 'b)
What is left? We still need a function of type ('a tree * 'b -> 'b) and we have the following that we haven't filled in our call to treefold: 'b -> 'a tree -> 'b. These are very similar, we just need to fix the argument order and uncurry the leftover part from treefold to pass it into foldr.
How do we manipulate treefold to pass it into foldr?

We need a function with parameters 'a tree and 'b, so start there.

	fun treefoldWrapper(tr, b) = ...

Now use treefold and f to implement it.

	fun treefoldWrapper(tr, b) = treefold f b tr
What are the final arguments to foldr? foldr treefoldWrapper (f(elt, base)) subTreeList
Stop and Participate -> Write the code for the recursive case.
How does it all come together?
	fun treefold f base Tree(elt, nil) = f(elt, base)
	|   treefold f base Tree(elt, subTreeList) = 
	      let
	         val fun treefoldWrapper (tr, b) = treefold f b tr
	      in
	         foldr treefoldWrapper (f(elt, base)) subTreeList
	      end

	
Stop and Participate -> Test treefold by using it in the example you implemented above (when we asked you to use treefold to implement one of the examples).