Class summary:   Aggregating Data
1 Computing the Product of a List of Numbers
2 Checking all Numbers Less than 10
2.1 The Case-based Definition of Lists
3 Leveraging the Cases of Lists to Write Our Functions

Class summary: Aggregating Data

Copyright (c) 2017 Kathi Fisler

Throughout this course, we’ve written programs that leverage the structure of data. We started with images, showing that the structure of code that builds a piece of data reflects the structure of the datum itself. We moved onto tables, writing programs to process the rows and cells that make up tables.

Now we have lists. Last lecture, we learned several built-in functions for processing lists. But the operations we learned only cover some of the computations we might want to do with lists. For example, what if we wanted the sum of a list of numbers? Or the concatenation of a list of strings? Or a flag made of stripes from a list of colors. In these cases, we are taking a list and \emph{aggregating} its contents into another piece of data. We need to learn how to write programs that aggregate data.

Once we cover this, we will have seen a range of operations that process data. Specifically, you’ll have seen how to:

This is a solid collection of data-manipulation tasks, all of which arise in real-world contexts.

If you want more discussion than what appears in our class notes, today’s material is covered very nicely in a chapter of a textbook by Brown CS Professor Shriram Krishnamurthi. These notes will highlight unique features of how we covered this material in class, but details will be in chapters 6.1 through 6.4.2 of PAPL chapter 6.

1 Computing the Product of a List of Numbers

Since summing a list is built-in in Pyret, let’s write code to produce the product of a list instead. What might the where: block look like?

  fun product(lst :: List<Number>) -> Number:

    ...

  where:

     product([list: 4, 7, 5]) is 4 * 7 * 5

     product([list: 7, 5]) is 7 * 5

     product([list: 5]) is 5

     product([list:]) is 1

  end

(why is the result of product 1 on the empty list? Because the value on the empty list should be the answer that doesn’t change the answer on the elements of the list.)

Stare at these examples – do you notice a pattern?

Yes, each where answer could be written using the line below it:

  where:

     product([list: 4, 7, 5]) is 4 * product([list: 7, 5])

     product([list: 7, 5]) is 7 * product([list: 5])

     product([list: 5]) is 5 * product([list:])

     product([list:]) is 1

  end

Do you see the pattern here? When we want to product a list, we can multiply the first item on the list to the product of the list with everything except the first item (that list is called the rest of the list). If we had an easy way to get at the first and rest parts of a list, our examples guide us to writing the function.

2 Checking all Numbers Less than 10

Let’s write the where clause for another problem:

2.1 The Case-based Definition of Lists

The pattern that we’ve identified shows that we can write an aggregation function by separating a list into its first and remaining elements. To see how to do that, it helps to understand how a list is built up.

When you write \code{[list: 4, 7, 5]), Pyret is actually turning it into a long-hand version under the hood. The long-hand version is summarized by the following statement:

  A List[Number] is either

  - empty, or

  - link(Number, List[Number])

The long-hand version of \code{[list: 4, 7, 5]) is

  link(4, link(7, link(5, empty)))

3 Leveraging the Cases of Lists to Write Our Functions

Combining this structure of lists with our examples, it feels like we want to implement product (roughly) like:

  if (the list is empty):

    1

  else:

    multiply the first by the product of the rest

To do this in code, we’ll use a construct called cases that has a built-in notion of lists. Here’s the final code. The lecture capture explains the parts.

  fun product(lst :: List<Number>) -> Number:

    cases (List) lst:

      | empty => 0

      | link(fst, rst) => fst + product(rst)

    end

  where:

    product([list: 4, 7, 5]) is 4 + 7 + 5

    product([list: 7, 5]) is 7 + 5

    product([list: 5]) is 5

    product([list:]) is 0

  end