Exam 3: Thinking recursively
Assigned: Monday, 6 November 2017
Prologue Due: Friday, 10 November 2017 by 10:30pm
Exam Due: Tuesday, 14 November 2017 by 10:30pm
Cover Sheet Due: Wednesday, 15 November 2017 by the start of class
Epilogue Due: Wednesday, 15 November 2017 by 10:30pm
Please read the exam procedures page for policies, turn-in procedures, and grading details. If you have any questions about the exam, check the Q&A at the bottom of this page. We will generally spend some time in class every day on questions and answers while the exam is in progress.
While the exam is out, please check back periodically to see if we have reported any new errata.
Complete the exam using the
exam03.rkt starter source
code. Please rename this file to 000000.rkt, but replace
000000 with your generated random number.
Prologue
The prologue for this examination will be via email, which you should send to your instructor. Your message should be titled CSC 151.03: Exam 3 Prologue (your name).
-
For each problem, please include a short note about something that will help you solve the problem. Mostly, we want to see some evidence that you’ve thought about the problem. You might note some similar procedures you’ve written or problems you’ve solved in the past (e.g., in a lab or on a homework assignment). You might note procedures that you expect to use. You might sketch an algorithm. You might pose a question to yourself. (We won’t necessarily read this in a timely fashion, so if you have questions for your instructor, you should ask by email or in person.)
If, when looking at a problem, you think you already know the answer, you can feel free to write something short like “solved” or “trivial”.
-
Which of those six problems do you expect to be the most difficult for you to solve? Why?
-
Conclude by answering the question What is an approach that you expect will help you be successful on this exam? For example, you might suggest that you will work thirty minutes on the exam each day, or work on the exam at 7pm each day, when your brain is most able to process information. (Note that it is perfectly fine to just repeat what you wrote in the epilogue for the prior exam.)
Epilogue
The epilogue for this examination will also be via email, which you should send to your instructor. Your message should be titled CSC 151.03: Exam 3 Epilogue (your name). Include answers to the following questions.
-
What was the most difficult part of the exam?
-
What made that part difficult?
-
What are two things you can do to be more successful on the next exam?
Problem 1: Tallying list elements
Topics: List recursion, Higher-order programming, Tallying
We have written a large number of procedures that tally values. At this
point, you should be ready to generalize those procedures. Document and
write a recursive procedure, (list-tally lst pred?) that counts the
number of elements in lst for which pred? holds.
> (list-tally (list 1 2 3 4 5) odd?)
3
> (list-tally (list 3 1.2 5/4 "hello") integer?)
1
> (list-tally (list 3 1.2 5/4 "hello" 7) integer?)
2
You may not use filter or length, even if you implement them yourself.
Problem 2: Setting elements in a list
Topics: direct recursion, list recursion, mutation (or simulated mutation)
Vectors have two primary benefits over lists: Vectors are mutable, in that you can change elements, and vectors support “random access”, which means that you can get and set elements quickly. We’ve also seen that the mutability of vectors can lead to some unexpected behavior of programs.
> (vector-set! some-animals 0 "aardvark")
> (vector-set! more-animals 0 "zebra")
> (if (string=? (vector-ref some-animals 0) "aardvark")
"starts with two a's" ; What I expect
"stripes")
"stripes" ; That's not what I expected.
Your task is to write an alternative to vector-set!. Document and write
a procedure, (list-set lst pos val), that takes a list, a position,
and a value as parameters and creates a new list in which the value
at the specified position is the specified value.
> (define some-animals (list "aardvark" "baboon" "chinchilla"))
> (list-set some-animals 0 "zebra")
'("zebra" "baboon" "chinchilla")
> some-animals
'("aardvark" "baboon" "chinchilla")
> (list-set some-animals 1 "yak")
'("aardvark" "yak" "chinchilla")
> (list-set some-animals 2 "xerus")
'("aardvark" "baboon" "xerus")
> some-animals
'("aardvark" "baboon" "chinchilla")
Please use list recursion. You may not convert the list to a vector.
You may not use take or drop.
Oh, if you’re wondering what happened in the vectors examples, we’d
defined some-animals and more-animals as follows.
> (define some-animals (make-vector 5))
> (define more-animals some-animals)
Problem 3: Is it a binary search tree?
Topics: trees, tree recursion, binary search trees
As we saw in the reading on binary trees and the corresponding lab, it can be useful to arrange values into a structure we call a “binary search tree”, or BST for short. A binary tree of strings is a binary search tree if tree is empty or (a) the left subtree is a binary search tree, (b) the right subtree is a binary search tree, (c) the string at the root alphabetically follows all strings in the left subtree; and (d) the string at the root alphabetically precedes all the strings in the right subtree.
Write, but do not document, a predicate, (bst? val) that holds only when
val is a binary search tree.
> (bst? empty)
#t
> (bst? (leaf "zebra"))
#t
> (bst? (leaf 5))
#f ; Contains a non-string value
> (bst? (vector "cat" "dog" "emu"))
#f ; Not a tree
> (bst? (node "dog"
(leaf "cat")
(leaf "squirrel")))
#t
> (bst? (node "dog"
(node "cat"
(leaf "bison")
empty)
(node "squirrel"
(leaf "emu")
(leaf "yak"))))
#t
> (bst? (node "dog"
(node "cat"
(leaf "bison")
empty)
(node "dingo"
(leaf "emu")
(leaf "yak"))))
#f ; "dingo" should be in the left subtree of "dog".
> (bst? (node "dog"
(node "cat"
(leaf "bison")
empty)
(node "squirrel"
(leaf "emu")
(leaf 7))))
#f ; Non-string in tree
Hint: For criterion (c), note that the string at the root alphabetically follows all the strings in the left subtree if either (i) the left subtree is empty or (ii) the string at the root follows the alphabetically last string in the left subtree. For criterion (d), note that the string at the root alphabetically precedes all the strings in the right subtree if either (i) the right subtree is empty or (ii) the string at the root precedes the alphabetically first string in the right subtree.
Problem 4: Selective vector updates
Topics: vector recursion, higher-order programming
Write, but do not document, a procedure, (vector-selective-map! vec
pred? proc), that updates all the elements in vec for which pred?
holds by applying proc. That is, vector-selective-map! is like
map!, but only for some of the members of vec.
> (define numbers (vector 1 2 3 4 5 6 7 8 9))
> (vector-selective-map! numbers odd? square)
> numbers
'#(1 2 9 4 25 6 49 8 81)
> (vector-selective-map! numbers (r-s < 7) increment)
> numbers
'#(2 3 9 5 25 7 49 8 81)
Note that the vector can have mixed values. If the vector has mixed values, there may be limits on what predicates will work, as some of the following examples suggest. You can assume that the predicate will work on each element of the list.
> (define stuff (vector 'a 2 "bee" (list "c" 2 3) 15 "more"))
> stuff
'#(a 2 "bee" ("c" 2 3) 15 "more")
> (vector-selective-map! stuff number? square)
> stuff
'#(a 4 "bee" ("c" 2 3) 225 "more")
> (vector-selective-map! stuff (conjoin number? odd?) increment)
> stuff
'#(a 4 "bee" ("c" 2 3) 226 "more")
> (vector-selective-map! stuff
(lambda (val)
(and (string? val) (char=? (string-ref val 0) #\b)))
string-upcase)
> stuff
'#(a 4 "BEE" ("c" 2 3) 226 "more")
> (vector-selective-map! stuff string? (r-s substring 1))
> stuff
'#(a 4 "EE" ("c" 2 3) 226 "ore")
> (vector-selective-map! stuff odd? square)
. . odd?: contract violation
expected: integer
given: "ore"
Note that our implementation of vector-selective-map! runs right to left.
Your error message may be different.
Problem 5: Counting letters
Topics: File recursion, Vectors
Write, but do not document, a procedure, (tally-letters-from-port port),
that takes as input an input port, reads all the characters from the port
and produces a vector giving a tally of the letters that appear.
> (tally-letters-from-port (open-input-string ""))
'#(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
> (tally-letters-from-port (open-input-string "triskaidekaphobia"))
'#(3 1 0 1 1 0 0 1 3 0 2 0 0 0 1 1 0 1 1 1 0 0 0 0 0 0)
> (tally-letters-from-port (open-input-string "a one and a 2 and something else!"))
'#(4 0 0 2 4 0 1 1 1 0 0 1 1 4 2 0 0 0 2 1 0 0 0 0 0 0)
> (define jane (open-input-file "/home/rebelsky/Desktop/pg1260.txt"))
> (display-tallies (tally-letters-from-port jane))
a: 63887
b: 11440
c: 19374
d: 37989
e: 102518
f: 17270
g: 15524
h: 46422
i: 57189
j: 1328
k: 6166
l: 32971
m: 22643
n: 55321
o: 61925
p: 12614
q: 958
r: 48590
s: 50882
t: 68646
u: 24008
v: 7743
w: 18986
x: 1306
y: 17634
z: 331
> (close-input-port jane)
As the example suggests, your procedure should not close the port; that responsibility is left to the client.
Problem 6: Whatzitdo
Topics: Vectors, Lists, Higher Order Procedures, Iteration
The following procedure performs a useful operation. While it’s a little difficult to tell what this procedure does, the original author clearly has his ducks in a row. Yes, I realize this is technically a column.
(define daffy
(lambda ( puddles )
(let ([ darkwing (lambda (
donald
huey
count-duckula ) (let ([
louie (vector-ref
donald
huey )][
dewey (vector-ref
donald
count-duckula )]) (vector-set!
donald
huey
dewey )(vector-set!
donald
count-duckula
louie )))]) (map (l-s
apply darkwing )(map list (make-list (quotient (vector-length
puddles ) 2)
puddles )(iota (quotient (vector-length
puddles ) 2))(map (r-s - 1) (map (l-s - (vector-length
puddles )) (iota (quotient (vector-length
puddles ) 2)))))))
puddles ))
Figure out what this procedure does, then do the following.
a. Rename the procedure and parameters so their types and purpose are clear.
b. Reformat the code so it is readable and properly indented. Remember that you can press ctrl+i to auto-indent the code.
c. Eliminate any unnecessary operations or redundant computations to make the code more concise.
d. Document the updated procedure.
Questions and answers
We will post answers to questions of general interest here while the exam is in progress. Please check here before emailing questions!
Initial questions and answers
These questions and answers were provided in the initial release of the examination.
- What is a general question?
- A question that is about the exam in general, not a particular problem.
- Do all the sections have the same exam?
- Yes.
- Can we still invoke the “There’s more to life” clause if we spend more than five hours on the exam?
- Yes. However, we really do recommend that you stop at five hours unless you are very close to finishing. It’s not worth your time or stress to spend more effort on the exam. It is, however, worth your time to come talk to us, and perhaps to get a mentor or more help (not on this exam, but on the class). There’s likely some concept you’re missing, and we can help figure that out.
- How do we know what our random number is?
- You should have received instructions on how to generate your random number on the day the exam was distributed. If you don’t have a number, ask your professor for one before submitting your exam.
- To show we’ve tested the code informally, would you just like us to just post the inputs we used to test the procedure? If so, how should we list those?
- Copy and paste the interactions pane into the appropriate place in the definitions pane. Select the text. Under the Racket menu, use “Comment out with semicolons.” You should have at least three examples or tests per procedure you write.
- Should we cite our partner from a past lab or assignment if we use code from a past lab or assignment?
- You should cite both yourself and your partner, although you should do so as anonymously as possible. For example “Ideas taken from the solution to problem 7 on assignment 2 written by student 641321 and partner.”
- If we write a broken procedure and replace it later, should we keep the previous one?
- Yes! This will help us give you partial credit if your final procedure isn’t quite right. But make sure to comment out the old one with semicolons and perhaps add a note or two as to what you were trying.
- Do I need to document helper procedures?
- You must document helpers using the 4Ps.
- If I write a helper procedure for one of the problems that does not require me to document the primary procedure, do I need to document the helper?
- Yes. All non-local helpers should be documented with the 4P’s.
- Do I have to document local helper procedures?
- It’s nice if you write a one-line comment that explains what they do, but you certainly don’t need 4P’s.
- What’s a local helper procedure?
- One defined within the procedure, most typically with a
letrecor a named let. - Can I use procedures that we have not covered in class?
- Probably not. Ask about individual procedures if you’re not sure.
- DrRacket crashed and ate my exam. What do I do?
- Send your instructor the exam file and any backups that DrRacket seems to have made. (Those tend to have a similar file name, but with pound signs or tildes added at the front or back.)
- How do I know when you’ve added a new question or answer?
- We try to add a date stamp to the questions and answers.
- Should we cite readings from the CSC 151 Web site? If so, how much information should we give.
- Yes. The title and URL should suffice.
- Can I use some other websites (different from our class websites) to solve problems in the exam?
- As the exam policies page states, “This examination is open book, open notes, open mind, open computer, open Web.” Note that it also states “ If you find ideas in a book or on the Web, be sure to cite them appropriately.”
- But be sure that the other Web sites rely on the procedures you’ve learned and follow the Scheme style we use.
- Do we have to cite the exam itself?
- No.
- How do I generate a random six digit number?
(random 1000000)- How do I submit my exam?
- Name it 012345.rkt (substituting your random number)
- Send it to your instructor, not the grader, as an attachment in an email message entitled “CSC 151 Exam 2” (or something similar)
- How many points will I lose if I don’t do the prologue?
- Somewhere between 2 and 5, depending on the mood of the grader.
- How many points will I lose if I do the prologue late?
- Somewhere between 0 and 5, depending on the mood of the grader.
- How many points will I lose if I don’t do the epilogue?
- Somewhere between 2 and 5, depending on the mood of the grader.
- How many points will I lose if I do the epilogue late?
- Somewhere between 0 and 5, depending on the mood of the grader.
- How many points will I lose if I don’t use the required template?
- Somewhere between 0 and 50, depending on the mood of the grader.
- Will I lose points if I fail to indent my code correctly with ctrl-i?
- Almost certainly.
- How many points will I lose if I fail to indent my code correctly?
- It depends on how hard your code is to read.
- Can you provide a sample time log?
- Sure.
; Time Log: ; Date Start Finish Elapsed Activity ; 2017-09-15 18:00 18:20 20 min Wrote four tests ; 2017-09-15 20:30 21:00 30 min Three non-working versions ; (marked as a, b, and c below). ; Decided to sleep on it. ; 2017-09-16 08:00 08:10 10 min Woke up with an idea. Coded it. ; it seems to work ; 2017-09-16 19:00 19:10 10 min A few more tests just to make ; sure. Done! - Do I have to use YYYY-MM-DD format?
- Certainly. Why would you use anything else?
- Do I get the bonus points for errors if I successfully invoke “There’s more to life”?
- Nope.
- Do I lose points for missing prologue/epilogue and other similar issues if I successfully invoke “There’s more to life”?
- Yes.
- What do you consider the hardest problem or problems on the exam?
- We’ll answer this question after all of the prologues are submitted.
New general questions
These questions and answers were added after the initial release of the examination.
Problem 1
- Should we consider predicates that are incompatible with the elements of the list? For example, a list
'(1 2 3 "hello" 4 5)and predicateodd?will fail on the element"hello". - You may add as a precondition that the predicate
pred?is compatible with all the elements of the list.
Problem 4
- At one point, you say that documentation is not necessary. In another, you suggest that we should explain things in the documentation. Which is it?
- No documentation is necessary.
Problem 5
- Should our procedure close the input port?
- No. Leave that to the client.
Errata
Please check back periodically in case we find any new issues.
- The starter code for
tally-letters-from-porthad a minor mistake and calledvectorinstead ofmake-vector. [HL, 1 point] - Unclear specifications on documentation on problem 4. [NC, 1 point]
- Sam used a Macintosh file path rather than a MathLAN path. [SL, 1 point]
Citations
Some of the problems on this exam are based on (and at times copied from) problems on previous exams for the course. Those exams were written by Charlie Curtsinger, Janet Davis, Rhys Price Jones, Titus Klinge, Samuel A. Rebelsky, John David Stone, Henry Walker, and Jerod Weinman. Many were written collaboratively, or were themselves based upon prior examinations, so precise credit is difficult, if not impossible.
Some problems on this exam were inspired by conversations with our students and by correct and incorrect student solutions on a variety of problems. We thank our students for that inspiration. Usually, a combination of questions or discussions inspired a problem, so it is difficult and inappropriate to credit individual students.