Assigned: Friday, November 4
Due: Friday, November 11 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 exam3.rkt starter source code. Please rename this file to 000000.rkt, but replace 000000 with your assigned random number.
Topics: Basic Recursion, Checking Preconditions, Lists
Write and document a procedure called (safe-average-a lst) that when given a list of numbers computes the average of those numbers. You must use direct recursion for this problem; that means you may not use a __-so-far parameter in a helper procedure. The procedure must also be safe in the sense that it verifies all preconditions and gracefully throws errors when they are not satisfied. The error messages must be informative and describe what the error was and where it occurred if applicable.
> (safe-average-a (list 1 2 3))
2
> (safe-average-a 5)
Error! safe-average-a: contract violation
Error! expected: list?
Error! given: 5
> (safe-average-a (list 1 2 3 "hello" 4))
Error! safe-average-a: list must contain all numbers; given "hello"
Topics: Tail Recursion, Checking Preconditions, Lists, Naming Local Procedures
Write but do not document a new version of your safe averaging procedure called (safe-average-b lst) using tail recursion. Any helper procedure(s) you use in this problem must be locally defined rather than globally defined. As before, the procedure must also verify all preconditions and give appropriate errors.
Topics: Map, Compose and Section, Lists, Characters and Strings
Some time ago, a student who I will call “Gle” came up with something like the following as an alternative to an existing procedure.
(define f (lambda (a b c) (list->string (map (compose (l-s string-ref a) (l-s + b)) (iota (- c b))))))
Figure out what the procedure does and then do the following.
a. Rename the procedure and the parameters so that their type and purpose is clear.
b. Reformat the code.
c. Document the updated procedure.
Topics: Numeric Recursion, Characters and Strings
While Scheme provides us with useful utilities that can turn numbers like 6 into the string "6" and back, we don’t have a function that can turn 6 into the cardinal "six" or 1342 into "one thousand three hundred forty two". While this is somewhat more complex than the conversion from 6 to "6", we can take advantage of the recursive structure of cardinal numbers.
First, cardinals can be “decomposed” three digits at a time. For example, the number 642 is "six hundred forty two", while the number 642000 is "six hundred forty two thousand", and 642000000 is "six hundred forty two million". If we add non-zero digits to the lower places, as in 642121, we simply concatenate these: 642 becomes "six hundred forty two", then we add "thousand" because these three digits occurred in thousands digits. Next, we append the converted value of 121—"one hundred twenty one"—to produce our final answer: "six hundred forty two thousand one hundred twenty one".
There are repeating patterns within these three digit blocks as well. The number 1 becomes "one", and the number 81 becomes "eighty one". As long as the tens place is not 1 we simply append the cardinal form of the ones place onto the end of the cardinal form of the tens place. Numbers from 11 to 19 will need to be handled as a special case. Hundreds work out very nicely: a number with a 5 in the hundreds place converts to "five hundred" followed by the converted form of the tens and ones places.
Write but do not document a procedure (number->words n) that takes an integer n between one and 999999999999999 (inclusive) and returns a string containing the cardinal form of the given number. You do not need to return "zero" for the input 0; we will only use counting numbers for this problem. Your procedure must use recursion to avoid unnecessary duplicated code. You may not write special cases for each of the sixteen digits.
You will need to extract specific digits from a decimal number to implement number->words.
To get the hundreds digit of a number n run (quotient n 100). Likewise, the tens digit is (quotient n 10).
You will also need to break a larger number up into three digit chunks. You can actually think of numbers a bit like lists. Imagine for a moment that we had a list of digits (list 210 543 876) that represent the number 876543210. The first element in the list is the ones through the hundreds place, the second element is the thousands through the hundred thousands place, and the third holds the millions through hundred millions digits.
With this list, we can access the first block of three digits with car and the remaining blocks of digits with cdr. Unfortunately our inputs are numbers and not lists, but we can use remainder and quotient with a divisor of 1000:
; Use remainder to get the rightmost three digits from a larger number
> (remainder 876543210 1000)
210
; Use quotient to get the rest of the digits
> (quotient 876543210 1000)
876543
> (number->words 1002)
"one thousand two"
> (number->words 414)
"four hundred fourteen"
> (number->words 999999999999999)
"nine hundred ninety nine trillion nine hundred ninety nine billion nine hundred ninety nine million nine hundred ninety nine thousand nine hundred ninety nine"
> (number->words 34029)
"thirty four thousand twenty nine"
Topics: Numeric Recursion
Write, but do not document, a procedure (my-remainder n d) that takes two integers as input and returns the remainder of n divided by d. The parameter n must be zero or more and the parameter d must be one or more. Note that you may not use the procedures remainder, quotient, /, or any other related procedures to solve this problem (in other words, you must use numeric recursion).
> (my-remainder 5 5)
0
> (my-remainder 574 232)
110
> (my-remainder 1 199)
1
Topics: Basic Recursion, Numeric Recursion, Turtle Graphics, Documentation
Write and document a procedure, (turtle-tree! turtle trunk-length branches levels), that uses the provided turtle to draw a tree.
You can think of a tree as a trunk (a line) with some number of smaller trees “growing” out of the end of the trunk.
To draw a tree, your turtle should rotate to a random angle between plus and minus 60 degrees, then draw a line that is trunk-length pixels long.
The end of the trunk should split into some number of smaller trees, determined by the branches parameter.
Each smaller tree should have a slightly shorter trunk length (75% of the previous trunk length works well).
This process should repeat until the tree has levels levels;
for example, if branches and levels are both 3, the tree should have a total of 13 lines.
Before drawing a tree, some set-up is required:
; Create a world, a turtle, set the brush to a fine line, and position the turtle
; at the bottom middle of the image facing up.
(define set-up-turtle
(lambda ()
(let* ([world (image-show (image-new 300 200))]
[tommy (turtle-new world)])
(turtle-turn! tommy -90)
(turtle-teleport! tommy 150 200)
(turtle-set-brush! tommy "2. Hardness 100" 0.15)
tommy)))
After running the set-up code above, you should be able to run the following examples with a completed turtle-tree! function.
While you should run the tests below, to earn full credit on this problem you must include three examples of your own.
Try varying the depth, branches, and trunk length to show how your tree procedure responds.
Include the resulting images in your exam file using the Insert Image option in DrRacket.
The following code should draw a tree with a trunk length of 60, three branches, and three levels.
> (turtle-tree! (set-up-turtle) 60 3 3)

This next example draws a four-level tree with five branches per level.
> (turtle-tree! (set-up-turtle) 60 5 4)

Here is another possible result of running the turtle-tree! procedure with the same parameters:
> (turtle-tree! (set-up-turtle) 60 5 4)

Finally, here is a five-level tree with seven branches per level. This one will take quite a while to finish drawing.
> (turtle-tree! (set-up-turtle) 60 7 5)

We will post answers to questions of general interest here while the exam is in progress. Please check here before emailing questions!
so-far parameter.let and let*?letrec and named let to define local helper procedures.cond or if with more than about ten cases, you’re probably approaching the problem in a way we would consider invalid. You should certainly use recursion. If we asked you to extend your procedure to support quadrillions and quintillions, this should be a very small change.n is zero?<, >, =, etc.). You may not use division, quotient, or remainder.turtle-tree! with zero levels?turtle-tree! do with zero branches?Please check back periodically in case we find any new issues.
defines for the two provided stub functions [TP, AM].my-remainder, but documentation was provided. [SF, +1 point]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, 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.