Due Monday, March 28, 4:30 PM
These exercises concern streams and delayed evaluation, and provide some practice using them in a non-trivial example.
One of the most well-known quotations in mathematical circles is Hardy's account of a visit he paid to Ramanujan. The quote is contained in footnote 70 on page 342 of the text, but I can't resist repeating it here:
| I remember once going to see him when he was lying ill at Putney. I had ridden in taxi-cab No. 1729, and remarked that the number seemed to me a rather dull one, and that I hoped it was not an unfavorable omen. `No,' he replied, `it is a very interesting number; it is the smallest number expressible as the sum of two cubes in two different ways.' |
The book calls numbers having this property Ramanujan numbers. (Actually, I have never seen this term used anywhere else, but the quote is so famous that any mathematician would know what is meant by it.) These exercises conclude with a remarkable method for calculating these numbers.
Put your answers to these problems in the file ram.scm in your ass4 directory. This file should begin with some definitions from the textbook, which are included here or for DrScheme users here. You should copy these definitions into the top of your file ram.scm (in addition to any other definitions you might find useful before starting the code for the various problems below). This is important: ram.scm should not load any other files -- it should be a self-contained file that includes any code it needs.
Also, if you find it convenient, you can create a separate text file notes.txt to hold more extended discussions of the problems and the code you wrote. (Of course, the code should still be commented in place.) You don't need to do this. But I will collect a file notes.txt if you have one.
(display-n stream n)that prints the first n elements of stream, each on a separate line.
(define ones (cons-stream 1 ones))Type in these definitions and verify that they work by using the display-n procedure. Generate the stream notdiv-235 of all integers that are not divisible by any of the numbers 2, 3, or 5. (Use stream-filter.)
(define integers (cons-stream 1 (add-streams ones integers)))
(define (interleave s t)This is like the procedure on page 341 of the text, but without the check for an empty stream, since we are assuming that both s and t are infinite streams.
(cons-stream (stream-car s)
(interleave t (stream-cdr s))))
Test the procedure by interleaving the stream of integers that are not divisible by 7 with the stream of integers not divisible by 3. Call this resulting stream nd73. What are the first few elements of the resulting stream? What expression did you evaluate to find this result?
(define (merge s1 s2)Use this procedure to merge the stream of integers that are not divisible by 7 with the stream of integers that are not divisible by 3. Call the stream you get in this manner nd73a. (This problem can of course be done more simply. But I want you to do it this way in preparation for later problems.)
(cond ((stream-null? s1) s2)
((stream-null? s2) s1)
(else
(let ((s1car (stream-car s1))
(s2car (stream-car s2)))
(cond ((< s1car s2car)
(cons-stream s1car (merge (stream-cdr s1) s2)))
((> s1car s2car)
(cons-stream s2car (merge s1 (stream-cdr s2))))
(else
(cons-stream s1car
(merge (stream-cdr s1)
(stream-cdr s2)))))))))
Give the name part-a to the stream which you produce for part a.
Give the name part-b to the stream which you produce for part b.
In doing this problem, note the following:
First of all, a stream (in this homework assignment) is always a stream of some kind of element. It may be a stream of integers, or a stream of pairs of integers, or a stream of lawnmowers, ... If it is, for instance, a stream of integers, then an element of the stream is an integer. If it is a stream of pairs of integers, then an element of the stream is a pair of integers, and so on.
Here is some clarification of the various functions that are constructed in the assignment:
merge: combines two streams of integers to form a new stream of integers. The two input streams are assumed to be ordered, and the output stream is created so that it is also ordered, and so that duplicates are discarded.
merge-weighted: This generalizes merge. Now the two input streams are streams of some kind of element (not necessarily of integers). Therefore, we have to have a way of determining the order. That is, integers come with a natural order already defined. But other kinds of objects (such as pairs of integers) do not have a natural order; one could imagine many different definitions of order for pairs of integers, each one having a different use.
Therefore, we need to specify the way we will compute the order relation for the elements in each of these streams.
We do this with a weight function (called weight).
An
element a of one of these streams comes before an element b
if (weight a) < (weight b). Thus,
the
difference between merge-weighted and merge is that
in merge-weighted
pairs: takes two streams (call them s and t) as input and forms a new stream (call it r). The elements of the stream r are pairs. The first component of each pair comes from s and the second comes from t. The resulting stream consists of all possible such pairs.
weighted-pairs: similar to pairs, except that the elements of r (which are themselves pairs of elements from s and t) are put into r in order of their weight.
In particular, merge and merge-weighted both take streams of a certain kind of element and return a stream of the same kind of element. The weight function in merge-weighted takes one argument -- an element of one of the streams -- and assigns it a weight. So for instance, if both streams were streams of integers, then merge and merge-weighted would both return a stream of integers (not of pairs of integers), and the weight function would simply take an integer as an argument.
pairs and weighted-pairs each take two streams of a certain kind of element and return a stream of pairs of those elements. So if each of the input streams were streams of integers, the result stream would be a stream of pairs of integers.
The code for weighted-pairs thus looks quite similar to the code for pairs. They both do more or less the same thing, except that weighted-pairs uses the weight function to determine the order of the pairs in the output stream. The weight function used in weighted-pairs takes a pair of elements as input and returns a weight value (typically an integer).
You should finish by constructing the stream ram, each of whose elements is a list. The first element of the stream ram is the list
(1729 (1 12) (9 10))
because 1729 = 13 + 123 = 93 + 103, and the other elements of the stream ram are built from the other Ramanujan numbers similarly.
You will undoubtedly find it useful to create one or more auxiliary functions in doing this problem. Please explain carefully in comments what you are doing.