Assigned: Monday, 27 April 2009
Due: Beginning of class, Monday, 4 May 2009
This is a take-home examination. You may use any time or times you deem appropriate to complete the exam, provided you return it to me by the due date.
There are 10 problems, each worth 10 points for a total of 100 points. Although each problem is worth the same amount, problems are not necessarily of equal difficulty.
Read the entire exam before you begin.
I expect that someone who has mastered the material and works at a moderate rate should have little trouble completing the exam in a reasonable amount of time. In particular, this exam is likely to take you about two to three hours, depending on how well you've learned the topics and how fast you work. You should not work more than four hours on this exam. Stop at four hours and write “There's more to life than CS” and you will earn at least 75 points on this exam.
I would also appreciate it if you would write down the amount of time each problem takes. Each person who does so will earn two points of extra credit. Since I worry about the amount of time my exams take, I will give two points of extra credit to the first two people who honestly report that they've spent at least three hours on the exam or completed the exam. (At that point, I may then change the exam.)
This examination is open book, open notes, open mind, open computer, open Web. However, it is closed person. That means you should not talk to other people about the exam. Other than as restricted by that limitation, you should feel free to use all reasonable resources available to you.
As always, you are expected to turn in your own work. If you find ideas in a book or on the Web, be sure to cite them appropriately. If you use code that you wrote for a previous lab or homework, cite that lab or homework and the other members of your group.
Although you may use the Web for this exam, you may not post your answers to this examination on the Web. And, in case it's not clear, you may not ask others (in person, via email, via IM, by posting a please help message, or in any other way) to put answers on the Web.
Because different students may be taking the exam at different times, you are not permitted to discuss the exam with anyone until after I have returned it. If you must say something about the exam, you are allowed to say “This is among the hardest exams I have ever taken. If you don't start it early, you will have no chance of finishing the exam.” You may also summarize these policies. You may not tell other students which problems you've finished. You may not tell other students how long you've spent on the exam.
You must include both of the following statements on the cover sheet of the examination.
Please sign and date each statement. Note that the statements must be true; if you are unable to sign either statement, please talk to me at your earliest convenience. You need not reveal the particulars of the dishonesty, simply that it happened. Note also that inappropriate assistance is assistance from (or to) anyone other than Professor Weinman (that's me) or Professor Davis.
You must present your exam to me in two forms: both physically and electronically. That is, you must write all of your answers using the computer, print them out, number the pages, put your name on the top of every page, and hand me the printed copy. You must also email me a copy of your exam. You should create the emailed version by copying the various parts of your exam and pasting them into an email message. In both cases, you should put your answers in the same order as the problems. Failure to name and number the printed pages will lead to a penalty of two points. Failure to turn in both versions may lead to a much worse penalty.
In many problems, I ask you to write code. Unless I specify otherwise in a problem, you should write working code and include examples that show that you've tested the code. Do not include images; I should be able to regenerate those.
Unless I explicitly ask you to document your procedures, you need not write introductory comments.
Just as you should be careful and precise when you write code and documentation, so should you be careful and precise when you write prose. Please check your spelling and grammar. Since I should be equally careful, the whole class will receive one point of extra credit for each error in spelling or grammar you identify on this exam. I will limit that form of extra credit to five points.
I will give partial credit for partially correct answers. I am best able to give such partial credit if you include a clear set of work that shows how you derived your answer. You ensure the best possible grade for yourself by clearly indicating what part of your answer is work and what part is your final answer.
I may not be available at the time you take the exam. If you feel that a question is badly worded or impossible to answer, note the problem you have observed and attempt to reword the question in such a way that it is answerable. Feel free to try to call me in the office (269-9812).
I will also reserve time at the start of classes next week to discuss any general questions you have on the exam.
Topics: List recursion, higher-order procedures, encapsulating control.
As you may recall, we began our investigation of recursion by considering how we might step through a list of numbers, adding them (or multiplying them or ...). Here are variants of the two versions we wrote:
One uses simple recursion.
(define sum-right
(lambda (numbers)
(if (null? (cdr numbers))
(car numbers)
(+ (car numbers) (sum-right (cdr numbers))))))
The other uses a helper to accumulate the results.
(define sum-left
(lambda (numbers)
(let kernel ((so-far (car numbers))
(remaining (cdr numbers)))
(if (null? remaining)
so-far
(kernel (+ so-far (car remaining))
(cdr remaining))))))
If you think carefully about it, the two processes compute results
slightly differently. Given the list (n0 n1 n2 n3 n4),
the first computes
(+ n0 (+ n1 (+ n2 (+ n3 n4))))
while the second computes
(+ (+ (+ (+ n0 n1) n2) n3) n4)
For addition, this doesn't make a difference. If the operation were subtraction, it would certainly make a difference.
The process of combining a list of values using a binary procedure
is quite common. We've seen it for addition, subtraction, and
multiplication.
We might also use it for appending strings and many other operations.
Computer scientists have various names for such a process. We'll call
it fold, but others use inject
(as in “inject this
operation into this list”)
or reduce (as in, “reduce this list to a
single value”).
Write a procedure, ( that mimics
fold-right
proc lst)sum-right, but with proc in place of
+. That is,
(fold-right should compute
proc (list v0 v1 v2 ... vn-1 vn))
(proc v0 (proc v1 (proc v2 (... (proc vn-1 vn) ... ))))
For example,
>(fold-right + (list 1 2 3 4))10>(fold-right * (list 1 2 3 4))24>(fold-right - (list 1 2 3 4))-2>(fold-right (lambda (s1 s2) (string-append s1 ", " s2)) (list "sentient" "malicious" "powerful" "stupid"))"sentient, malicious, powerful, stupid">(fold-right list (list 'a 'b 'c 'd 'e))(a (b (c (d e))))
Write a procedure, ( that mimics
fold-left
proc lst)sum-left, but with proc in place of
+. That is,
(fold-left should compute
proc (list v0 v1 v2 ... vn-1 vn))
(proc (proc ... (proc (proc v0 v1) v2) ... vn-1) vn)
For example,
>(fold-left + (list 1 2 3 4))10>(fold-left * (list 1 2 3 4))24>(fold-left - (list 1 2 3 4))-8>(fold-left (lambda (s1 s2) (string-append s1 ", " s2)) (list "sentient" "malicious" "powerful" "stupid"))"sentient, malicious, powerful, stupid">(fold-left list (list 'a 'b 'c 'd 'e))((((a b) c) d) e)
Topics: Deep recursion, pair structures, predicates.
You have written a predicate color-tree? in
the lab on trees that determines whether every leaf in a tree is a
color. Generalize this idea by writing the predicate
(tree-all? pred?
tree), which indicates whether the
predicate pred? is satisfied for all leaves
in tree.
For example,
>(tree-all? number? (cons (cons (cons 1 2) (cons 3 4)) (cons 5 (cons 6 7))))#t>(tree-all? even? (cons (cons (cons 1 2) (cons 3 4)) (cons 5 (cons 6 7))))#f>(tree-all? char? (cons (cons #\a #\b) #\c))#t>(tree-all? char? (cons (cons #\a #\b) "c"))#f>(tree-all? odd? 1)#t>(tree-all? char? 1)#f
Topics: Higher-order procedures, searching, files.
Write, but do not document, a procedure,
(, that searches the specified file
for the first value that matches file-search filename
pred?)pred?. For
example, if the file contains the values
12 1 23 152 6
then we might see the following results.
>(file-search "sample-file" odd?)1>(file-search "sample-file" (lambda (x) (> x 100)))152>(file-search "sample-file" (lambda (x) (< x 0)))#f>(file-search "sample-file" string?)#f
Topics: Strings, numeric recursion.
As you may recall, ( extracts a substring
from substring
str start
finish)str that runs from position
start to position
finish-1. The string-length
procedure lets you find the length of a string.
Write, but do not document, a procedure,
(, that generates a list of all
the substrings of all-substrings str
len)str of length
len. For example,
>(all-substrings "television" 3)("tel" "ele" "lev" "evi" "vis" "isi" "sio" "ion")
Topics: Unit testing, numeric recursion, strings.
When a procedure passes a series of tests, can you be certain the procedure is implemented correctly? Unfortunately not. Consider the following procedure and test suite.
;;; Procedure:
;;; string-trim
;;; Parameters:
;;; str, a string
;;; Purpose:
;;; Remove leading and trailing whitespace from a string.
;;; Produces:
;;; trimmed, a string
;;; Preconditions:
;;; No additional.
;;; Postconditions:
;;; trimmed is a substring of str, which can be obtained by
;;; (substring str start end) for some values of start and end.
;;; (string-ref str start) is not a whitespace character.
;;; (string-ref str end) is not a whitespace character.
;;; For all i, 0 <= i < start, (string-ref str i) is a whitespace character.
;;; For all j, end < j < (string-length str), (string-ref str j) is a whitespace character.
;;; Note that if str consists only of whitespace, then trimmed should be "".
(define string-trim
(lambda (str)
(letrec ((find-first-non-whitespace
(lambda (start end)
(cond ((= start end) start)
((not (char-whitespace? (string-ref str start))) start)
(else (find-first-non-whitespace (+ start 1) end)))))
(find-last-non-whitespace
(lambda (start end)
(cond ((= end start) end)
((not (char-whitespace? (string-ref str end))) (+ end 1))
(else (find-last-non-whitespace start (- end 1)))))))
(let ((start (find-first-non-whitespace 1 (- (string-length str) 1)))
(end (find-last-non-whitespace 1 (- (string-length str) 1))))
(substring str start end)))))
(begin-tests!)
(test! (string-trim " spacey ") "spacey")
(test! (string-trim "\ttabs\t\t") "tabs")
(test! (string-trim "\nnewlines!\n") "newlines!")
(test! (string-trim " spaces in the middle ") "spaces in the middle")
(test! (string-trim " ") "")
(end-tests!)
Although the procedure passes the given tests, it is not correctly implemented. Write additional tests that reveal the bugs in the procedure. That is, write tests that the procedure fails, but that it would pass if it were implemented correctly according to the documentation.
Hint: You should be able to find at least one case where the procedure call fails because of an error, and at least one case where the procedure returns an incorrect result.
Topics:
Higher-order programming,
map,
apply,
conciseness.
A very useful mathematical identity is the fact that the log of the
product of numbers is equal to the sum of the log of the
numbers. You may have seen the special case for two numbers,
log(a*b) = log(a) + log(b), but this generalizes to any number of values. In the following, you will use the built-in Scheme procedure log.
Assume that numbers is defined as a list of
numbers.
Write a Scheme expression that first computes the product of
numbers
and then takes the log of
that product. (You should not use lambda in your
expression.)
That is, if numbers is defined as
(list 1 2 3 4),
your expression should calculate log(1*2*3*4).
Write a Scheme expression that calculates the mathematical
equivalent of the previous expression: first take the log of
each number in the list numbers, and then sum
these log values. (Again, you should not use lambda
in your expression.)
That is, if numbers is defined as
(list 1 2 3 4), your
expression should calculate log(1)+log(2)+log(3)+log(4).
Topics: Sectioning, composition, characters, strings.
On the previous exam, you wrote a set of procedures for ROT13, a specific kind of Caesar substitution cipher. Presumably, you wrote a set of support procedures not unlike the following.
(define shift-down (lambda (x) (- x 65))) (define add-13 (lambda (x) (+ 13 x))) (define wrap-26 (lambda (x) (mod x 26))) (define shift-up (lambda (x) (+ 65 x)))
You used these to write char-rot13. In this
problem, we will generalize this further, and strive to make our
solution more concise.
Write a
procedure (
that returns a procedure for encoding a
character with a Caesar cipher using an arbitrary offset (i.e., not
just 13).
char-caesar-encoder offset)
>(define char-rot15 (char-caesar-encoder 15))>(char-rot15 #\B)#\Q>(define char-unrot15 (char-caesar-encoder -15))>(char-unrot15 #\Q)#\B>((char-caesar-encoder 1) #\A)#\B
You should strive to make your solution as concise as possible. You may not define any other helper procedures (including the ones given above from the ROT13 problem).
Using your procedure char-caesar-encoder from
part (a) of this problem, write a procedure
( that takes a
string string-caesar-encode
str offset)str and encodes it using a Caesar cipher
with offset as the rotation amount.
>(string-caesar-encode "HELLO" 7)"OLSSV">(string-caesar-encode (string-caesar-encode "WORLD" 10) -10)"WORLD"
You should strive to make your solution as concise as possible.
Topics: Colors, iterating over images, optimal values.
You may recall that in the past, we looked for the brightest
colors in a list of colors. It might make more sense to look for
the brightest pixel in an image. Write, but do not document,
a procedure, ( that returns the position of the
brightest pixel. The position should be a pair of the form
image-brightest-pixel
image)(col . row).
Warning: This procedure may be quite slow even if correctly implemented. Test it on a very small image (e.g., 5x5 pixels) before trying it on a larger image.
For example, if square is defined as the image shown, then
calling image-brightest-pixel should produce the
following result.
>(image-brightest-pixel square)(2 . 1)
In writing image-brightest-pixel, you may find
the following procedure helpful.
;;; Procedure:
;;; rgb-brightness
;;; Parameters:
;;; color, an RGB color
;;; Purpose:
;;; Computes the brightness of color on a 0 (dark) to 100 (light) scale.
;;; Produces:
;;; b, an integer
;;; Preconditions:
;;; color is a valid RGB color. That is, each component is between
;;; 0 and 255, inclusive.
;;; Postconditions:
;;; If color1 is likely to be perceived as lighter than color2,
;;; then (brightness color1) > (brightness color2).
(define rgb-brightness
(lambda (color)
(round (* 100 (/ (+ (* 0.30 (rgb-red color))
(* 0.59 (rgb-green color))
(* 0.11 (rgb-blue color)))
255)))))
Write 6-P documentation for the
image-brightest-pixel procedure.
Hint: Think carefully about the postconditions.
What does it mean for a pixel to be the brightest?
Topics:
Vectors,
color palettes,
image-scan.
As you may know, a histogram measures the frequency of occurrence of different types of items in a set. For example, we might survey the class and produce a histogram that gives the number of first-years, second-years, juniors, and seniors, or the number of science, social studies, humanities, and other majors. Machine vision researchers (and others) are sometimes concerned with the frequency at which each color occurs in an image. Since the number of possible colors is so huge, they use a smaller palette to categorize each color in the image. In this problem, we'll use a palette to encode each pixel in an image, and count the number of times each palette index occurs.
Recall the procedure (palette-encode-color
palette color), which
encodes a color by returning the index of the closest color from the
palette.
The function image-scan allows us to easily
iterate over all the pixels of an image. The usage of the
procedure is
(. The
procedure image-scan
image
proc!)proc! should have the
form (lambda (col row color) ...). Like its
kin image-variant
and image-compute, the
procedure image-scan calls your
procedure proc! for every pixel in the image,
in this case passing to proc! the column,
row, and color of the pixel.
Use image-scan and
palette-encode-color to implement the
procedure documented as follows.
;;; Procedure: ;;; image-palette-histogram ;;; Parameters: ;;; image, an image identifier ;;; palette, a list of RGB colors ;;; Purpose: ;;; To count the number of times each palette-encoded color is used in an image ;;; Produces: ;;; hist, a vector ;;; Preconditions: ;;; palette is non-null ;;; Postconditions: ;;; The length of hist is the same as the length of palette. ;;; Each index in hist contains the number of times the corresponding palette ;;; color is used in the image.
You can copy the palette-encode-color and
related procedures from here. You do not
need to include them in your solution.
Hint: You will need to create a vector and use
it in the body of the procedure passed
to image-scan. Each time you encode a color
from the image, increment the value in the vector that corresponds
to the resulting index.
Some example runs are below:
>(define colors-rainbow (map color-name->rgb (list "red" "orange" "yellow" "green" "blue" "indigo" "violet")))colors-rainbow>(image-palette-histogram (image-load "/usr/share/pixmaps/fsview.xpm") colors-rainbow)#(0 222 0 288 154 40 320)>(image-palette-histogram (image-load "/usr/share/pixmaps/gnome-error.png") colors-rainbow)#(575 182 0 1437 0 87 23)>(image-palette-histogram (image-load "/usr/share/pixmaps/python.xpm") colors-rainbow)#(0 227 260 471 0 472 91)
Here we will post answers to questions of general interest. Please check here before emailing your questions!
Here you will find errors of spelling, grammar, and design that students have noted. Remember, each error found corresponds to a point of extra credit for everyone. We usually limit such extra credit to five points. However, if we make an astoundingly large number of errors, then we will provide more extra credit.
image-brightest-pixel was misspelled as
image-brighest-pixel in Problem 8. [JMK, 1 point]
number-tree? It should
have referenced color-tree?. [JJW and DBD, 1 point]
color-name->rgb was
misspelled. [WT, 1 point]