Lecture 4

Administrivia

CS Jobs Board

This is a job board specifically for CS students. We point all recruiters and employers to this job board, and new posts come up on a regular basis. All job postings are public, and you do not need an account to access them. However, if you log into the job board with your CNetID, you will be able to sign up for notifications of new job postings.

CS Student Organizations

There are three student organizations on campus that cater to technical students: hack@uchicago (http://hack.uchicago.edu/), the ACM Student Chapter (http://acm.cs.uchicago.edu/), and an ACM-W Student Chapter (the ACM's Committee on Women in Computing). All three of these organizations hold events regularly.

Most of these events are announced on the ACM mailing list:

Events are also posted on the CS Student Activities Calendar (see below)

CS Student Activities Committee

The Computer Science Student Activities Committee is a committee in the Department of Computer Science in charge of facilitating the organization of student-oriented events in the department, as well as fundraising for student activities. The committee is composed of undergraduate, MS, and PhD students in Computer Science.

The committee often acts as a "frontend" for the student organizations listed above. In particular, it provides a central calendar with all their events:

You can add this calendar to your Google Calendar through this link:

CSIL Minicourses

There have been some changes in the times of the CSIL minicourses. Please note that

For more information, go to http://csil.cs.uchicago.edu.

Natural Recursion

I've spoken about the notion of a natural recursion. At least one way to think of this is that we'll take a constructed term (e.g., a list), and replace each of the constructors one-for-one with a function. For example, let's consider the pedantic form of the list [1..5],

1 : (2 : (3 : (4 : (5 : []))))

If we want to sum the numbers from 1 through 5, we can imagine replacing each : by a +, and [] by 0, i.e.,

1 + (2 + (3 + (4 + (5 + 0))))

If we generalize away from the specifics of addition and zero, we arrive at the general purpose recursive definition function for lists, traditionally known as foldr, for “fold right”:

foldr combiner base [] = base foldr combiner base (x:xs) = combiner x (foldr combiner base xs)

The foldr function can be used to give concise definitions of other standard functions, e.g.,

sum = foldr (+) 0 filter p = foldr p' [] where p' x xs | p x = x : xs | otherwise = xs

Note two things here. The first is that Haskell permits the use of apostrophes/primes in variable names, reflecting common mathematical practice. Also note the use of a where clause, whereby we can introduce local definitions. This is, by the way, a very common use of where, i.e., setting up a definition of a special-purpose function to pass as an argument to one of the standard higher-order functions. Note that I didn't ask you to note the eta-reduced first line ;-).

*Exercise 4.1 Provide foldr-based implementations of product and map.

Note: It's possible to provide a fully point-free definition of map based on foldr, but it crosses the line from elegant to inscrutable and maybe even a bit disturbing:

map = flip foldr [] . ((:) .)

There is a general theory at play here, and with a little bit of practice and an algebraic turn of mind, it's not too difficult to begin deriving expressions like this. At one level, this is a good discipline, and occasionally even useful if it facilitates an opportunistic substitution, but at another, good taste needs to enter in. Does eta-reducing map all the way to a point free composition of Prelude functions lead to increased clarity or understanding? Not for me. If I think through the definition carefully, I can see why it works, but I've yet to receive any additional enlightenment from contemplating this definition.

*Exercise 4.2 Write primrec, the natural recursive function generator for NaturalNumber. Give implementations of (+) and (*) based on primrec.

Hint: your definitions can be one-liners of the form

x + y = primrec ...

List Comprehension Notation

Mathematicians have a concise notation for describing a set, which typically involves describing an initial set, a predicate, and a form for elements of that set, e.g., $\{ x^2 \mid \hbox{$x \in \omega$ and $x$ is even}\}$, the squares of all even natural numbers. These are called set comprehensions. Haskell provides a similar notation for lists:

> [x^2 | x <- [1..10], even x] [4,16,36,64,100]

It is possible in list comprehensions to have multiple generators, let bindings, and to interleave generations, tests, and bindings. As a simple example, let's generate all of the pythagorean triples that where the hypotenuse is less than a given number:

pythagoreanTriples n = [(a,b,c) | a <- [1..n], b <- [a+1..n], c <- [b+1..n], a^2 + b^2 == c^2] > pythagoreanTriples 20 [(3,4,5),(5,12,13),(6,8,10),(8,15,17),(9,12,15),(12,16,20)]

Note that the results are sorted by a, because the generation runs like this, for each a from 1 to n, generate b from a+1 to n, and for each such a and b, generate c from b+1 to n...

Also note our use of tuples. Tuples look like lists, but they're actually very different. We'll talk about them more next lecture, but for now, they're a convenient way to package up a few values.

But notice that this list contains a few non-primitive triples, e.g., (6,8,10), (9,12,15), and (12,16,20), which are all multiples of (3,4,5). Suppose we wanted to restrict ourselves to primitive triples. How might we do that? A simple approach would be to filter out those triples where a and b have a non-trivial common divisor, i.e.,

primitiveTriples n = [(a,b,c) | a <- [1..n], b <- [a+1..n], gcd a b == 1, c <- [b+1..n], a^2 + b^2 == c^2]

Note that it is more efficient to do the gcd test before the generation of c, because otherwise we'd have to repeat the same test (on a and b alone) for each c; but it should be noted too that our basic computational plan emphases clarity over efficiency, and there are much more efficient ways to generate lists of pythagorean triples.

Standard Functions

There are a number of extremely useful list-manipulation functions to be found in both the Prelude and in Data.List. I'll point specifically to (++), null, (!!), reverse, concat, concatMap, maximum, minimum, replicate, take, drop, elem, zip, zipWith, unzip from the Prelude; and nub and sort from Data.List.

*Exercise 4.3 Find the documentation for various Haskell modules, and read the documentation on the Prelude and Data.List. Don't take shortcuts here, and be disciplined. Programming in the real world involves reading a fair amount of documentation, and it is a skill you'll need to master. As it happens, if you've installed the Haskell Platform on your system, the documentation should be available there. You'll want to bookmark it!

There is an especially pleasing definition of concat using list comprehension:

concat xss = [x | xs <- xss, x <- xs]

Make sure you understand this definition. Then, once you've done that, look at the actual implementation of concat in the Haskell system (note that there is a “source” link associated with each function's documentation. This takes you straight to the language's implementation of the function!) You'll probably note that the GHC source files don't look like ours—they have a lot more comments, and there's a good deal of syntax that's targeted towards producing documentation. Yes, the documentation files are built directly from these sources too.

Anyway, you'll discover that the GHC implementation of concat isn't the list comprehension definition. What is it?

Exercise 4.4 Revisit Exercise 3.2, using list comprehension. You should be able to write code that doesn't rely on allp or filterAll, yet which is at least as easy as the filterAll-based solution to maintain.