Functional Problem Solving (CSC 151 2013F) : Assignments

Exam 2: Control and Beyond


Assigned: Wednesday, 30 October 2013

Due:

Preliminaries

Exam format

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.

This examination has a prologue that must be completed by Friday afternoon. The prologue is intended to help you get started thinking about the examination. The prologue is optional. However, if you intend to invoke the “there's more to life” option, you must turn in the prologue by the specified deadline.

There are 10 problems on this examination. Each problem is 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.

We 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 three to four hours, depending on how well you've learned the topics and how fast you work. You should not work more than five hours on this exam. Stop at five hours and write “There's more to life than CS” and you will earn at least 70 on this exam, provided you filled in the prologue by the specified deadline. You should count the time you spend on the prologue in the five hours. With such evidence of serious intent, your score will be the maximum of (1) your actual score out of 100, (2) the average of your best 7 answers scaled to a maximum of 90, or (3) 70. The bonus points for errors are usually not applied in such circumstances.

We will give two points of extra credit to the first two people who honestly report that they have completed the exam in four hours or less or have spent at least four hours on the exam. In the latter case, they should also report on what work they've completed in the four hours. After receiving such notices, we may change the exam.

Academic honesty

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 any students who worked with you. If you use code that you found on the course Web site, be sure to cite that code. You need not cite the code provided in the body of the examination.

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, via IRC, 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.” 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.

  1. I have neither received nor given inappropriate assistance on this examination.
  2. I am not aware of any other students who have given or received inappropriate assistance on this 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 Rebelsky.

Presenting Your Work

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.

While your electronic version is due at 10:30 p.m. Tuesday, your physical copy will be submitted in class on Wednesday. It is presumed the physical copy matches the electronic copy. Any discrepancies (other than formatting) will be considered a misrepresentation of your work and referred to the Committee on Academic Standing.

In many problems, we ask you to write code. Unless we specify otherwise in a problem, you should write working code and include examples that show that you've tested the code. You should use examples other than those that may be provided with the exam. Do not include images; we should be able to regenerate those.

Unless we 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. Because we 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. We will limit that form of extra credit to five points.

We will give partial credit for partially correct answers. We are 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.

Getting Help

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. If it's a reasonable hour (8am-10pm), feel free to try to call me (cell phone - 641-990-2947).

I will also reserve time at the start of each class before the exam is due to discuss any general questions you have on the exam.

Problems

Part A: Transformations

Problem 1: Combining Colors

Topics: RGB colors, Procedures, Numbers

Sometimes we want to take two RGB colors and blend them in some way. For example, we might blend colors “smoothly” by taking an average over their components. At other times we may want more drastic combinations. For example, we might want to use only the more intense or the less intense of each color component.

Write a procedure, (rgb-min-blend color1 color2), that takes two RGB colors, and creates a new RGB color where each component (red, green, and blue) is the smaller of the corresponding component of the two colors.

> (rgb->rgb-list (rgb-min-blend (color->rgb "white") (color->rgb "black")))
(0 0 0)
> (color->rgb-list "mistyrose")
(255 228 225)
> (color->rgb-list "lemonchiffon")
(255 250 205)
> (rgb->rgb-list (rgb-min-blend (color->rgb "mistyrose") (color->rgb "lemonchiffon")))
(255 228 205)
> (color->rgb-list "springgreen")
(0 255 127)
> (color->rgb-list "bisque")
(255 228 196)
> (rgb->rgb-list (rgb-min-blend (color->rgb "springgreen") (color-name->rgb "bisque")))
(0 228 127)

Problem 2: Making Colors Greyer

Topics: RGB colors, color transformations, local bindings, code reading, concision

Consider the following procedure.

;;; Procedure:
;;;   rgb-greyer
;;; Purpose:
;;;   To make a color greyer while preserving its hue.
;;; Parameters:
;;;   color, an rgb color
;;;   weight, a real number
;;; Produces:
;;;   greyer, an rgb color
;;; Preconditions:
;;;   0 <= weight <= 1
;;; Postconditions:
;;;   If weight is 0, then greyer is equal to color
;;;   If weight is 1, then greyer is equal to (rgb-greyscale color)
;;;   (rgb->hue greyer) = (rgb->hue color)
;;;     (unless weight is 1; then the hue of greyer will be 0)
;;;   (rgb->value greyer) <= (rgb->value color)
;;;   (rgb->saturation greyer) <= (rgb->saturation color)
;;; Process:
;;;   Each component of the color is blended with the color's grey value
;;;     in proportion to weight.
(define rgb-greyer
  (lambda (color weight)
    (rgb-new (+ (* (- 1 weight) (rgb-red color))
                (* weight
                   (+ (* 0.30 (rgb-red color))
                      (* 0.59 (rgb-green color))
                      (* 0.11 (rgb-blue color)))))
             (+ (* (- 1 weight) (rgb-green color))
                (* weight
                 (+ (* 0.30 (rgb-red color))
                    (* 0.59 (rgb-green color))
                    (* 0.11 (rgb-blue color)))))
             (+ (* (- 1 weight) (rgb-blue color))
                (* weight
                   (+ (* 0.30 (rgb-red color))
                      (* 0.59 (rgb-green color))
                      (* 0.11 (rgb-blue color))))))))

Notice that this procedure is very repetitive. Using let and/or let*, rewrite this procedure as concisely as you can.

Problem 3: Making Images Greyer

Topics: Transforming images, anonymous procedures, sectioning

Write, but do not document, a procedure (image-greyer image weight), which transforms all of the pixels in image using rgb-greyer. Note that providing 0 for the weight parameter will result in no change to the image, whereas providing 1 will result in a greyscale image.

Write this procedure as concisely as you can.

Part B: Turtles and Iteration

Problem 4: Sequences of Squares

Topics: Turtles, iteration

Consider the following procedure, which uses the given turtle and side length to draw a single square.

(define turtle-square!
  (lambda (turtle side-length)
    (repeat 4 (lambda () 
                (turtle-forward! turtle side-length)
                (turtle-turn! turtle 90)))))

What if we wanted to draw a sequence of squares, like so?

Write, but do not document, a procedure, (turtle-square-sequence! turtle copies side-length spacing), that draws the specified number of squares, each of the specified size. Each square should be offset from the previous square by the specified spacing.

You need not return the turtle to its original position and orientation.

You may not use recursion to solve this problem. You must use for-each or repeat. You should use the given turtle-square! procedure.

Your procedure should produce results similar to the following examples.

(define world (image-show (image-new 300 50)))
(define tia (turtle-new world))
(turtle-teleport! tia 10 10)
(turtle-square-sequence! tia 12 10 10)
(define world (image-show (image-new 300 50)))
(define tia (turtle-new world))
(turtle-teleport! tia 10 10)
(turtle-square-sequence! tia 8 30 5)
(define world (image-show (image-new 200 200)))
(define tia (turtle-new world))
(repeat 6 (lambda ()
           (turtle-teleport! tia 100 100)
           (turtle-turn! tia 60)
           (turtle-square-sequence! tia 5 10 10)))

Problem 5: Spacing out Sequences of Squares

Topics: Turtles, iteration

Now suppose we want to extend our sequence of squares so that the spacing between squares systematically varies, like so:

Write, but do not document, a procedure, (turtle-square-progression! turtle copies side-length spacing-factor) that draws the specified number of squares, each of the specified size. The second square should be offset from the first by a distance of spacing-factor. Each subsequent square should be offset by an additional distance of spacing-factor.

You need not return the turtle to its original position and orientation.

You may not use recursion to solve this problem. You must use for-each or repeat. You should use our definition of turtle-square! from the previous problem.

Your procedure should produce results similar to the following examples.

(define world (image-show (image-new 300 50)))
(define tia (turtle-new world))
(turtle-teleport! tia 10 10)
(turtle-square-progression! tia 7 10 10)
(define world (image-show (image-new 300 50)))
(define tia (turtle-new world))
(turtle-teleport! tia 10 10)
(turtle-square-progression! tia 6 30 5)
(define world (image-show (image-new 200 200)))
(define tia (turtle-new world))
(repeat 6 (lambda ()
           (turtle-teleport! tia 100 100)
           (turtle-turn! tia 60)
           (turtle-square-progression! tia 5 10 5)))

Part C: Lists

Problem 6: rac and rdc

Topics: Core list operations, Recursion

You know that car and cdr are two basic operations for extracting the first element of a list and all but the first element of a list. Surprisingly, Scheme doesn't have procedures for getting the last and all but the last element from a list. (Okay, it's not so surprising: The underlying implementation of lists makes implementing car and cdr quick and straightforward. But the implementation makes getting the last element much less efficient.)

Using recursion (direct or helper), implement two procedures, rac and rdc, that get the last element of a list and all but the last element of the list. Hints for the implementation can be found in the Process section of the 6+P documentation below. Your procedures need not check preconditions.

In writing rac and rdc, the only list procedures you may use are car, cdr, cons, and null?. You may also use the special value null.

;;; Procedure:
;;;   rac
;;; Parameters:
;;;   lst, a nonempty list
;;; Purpose:
;;;   Find the last element of lst.
;;; Produces:
;;;   last, a value
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   last is the last element of lst.
;;; Process:
;;;   Repeatedly take the cdr until we reach the last element.

;;; Procedure:
;;;   rdc
;;; Parameter:
;;;   lst, a nonempty list
;;; Purpose:
;;;   Create a new list by removing the last element of lst
;;; Produces:
;;;   newlst, a list
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   (length newlst) = (- (length lst) 1)
;;;   For all i, 0 <= i < (- (length lst) 1)
;;;     (list-ref newlst i) = (list-ref lst i)
;;; Process:
;;;   Repeatedly take the cdr until we reach the last element.
;;;   Discard it.  Then either build the list back up, or deal
;;;   with the list we've accumulated along the way.

Problem 7: rac and rdc, Revisited

Topics: Lists, Problem solving, Precondition testing

In the previous problem, we asked you to write two procedures using only the basic list operations so that you could think about the underlying design of lists. Using those basic list operations required you to explicitly recurse through the list.

However, it is possible to write both rac and rdc much more concisely if you rely on some of the other list operations.

Write versions of rac and rdc that do not use explicit recursion. This time, both procedures should check their preconditions (in particular, the precondition that the parameter is a nonempty list) and report an appropriate error if the preconditions are not true.

Problem 8: An increasing? Predicate

Topics: Recursion, predicates, and, or

Write and document a procedure, (increasing? numbers), that returns #t if every number in the list (except the last) is less than or equal to the number immediately after it, and returns #f otherwise.

> (increasing? (iota 10))
#t
> (increasing? (reverse (iota 10)))
#f
> (increasing? (list 1/10 1/2 1/3 5/6))
#f
> (increasing? (list 0 1 1 2 3 3 10))
#t

While your inclination may be to write this procedure using if (or when, or cond), for full credit you should avoid using if (or when or cond).

Part D: Clarity

Problem 9: Ordering Operations for Clarity

Topics: Higher-order programming, Order of evaluation, Drawings as values, Code clarity

As you may have noted, one of the issues of the way we designed the drawing procedures is what we call “the German problem” - we have to wait a long time for an important part of information (in German, it's the verb; in these procedures, it's typically the amount by which we're transforming a drawing).

However, higher-order approaches, such as composition and sectioning, can help clarify things. For example, consider the following procedure, that scales a drawing, shifts it, and recolors it.

(define drawing-munge
  (lambda (drawing)
    (drawing-hshift
     (drawing-vshift
      (drawing-scale
       (drawing-recolor
        drawing
        "red")
      2)
     20)
    30)))

Using the higher-order procedures, we can write this somewhat clearer version.

(define drawing-munge
  (let ([munge (o (r-s drawing-hshift 30)
                  (r-s drawing-vshift 20)
                  (r-s drawing-scale 2)
                  (r-s drawing-recolor "red"))])
    (lambda (drawing)
      (munge drawing))))

As you will note, the operations are clearer: horizontally shift by 30, vertically shift by 20, scale by 2, and recolor red. It's still a bit awkward that we do them in bottom-to-top order, but that's something we get used to.

You may recall the smaller-right-neighbor procedure that you've written in labs and on a previous exam. That procedure creates a version of the drawing that is scaled by 75%, immediately to the right of the original, and with the same top.

Re-write the smaller-right-neighbor procedure using composition and sectioning, as suggested above.

Problem 10: Decoding Code

Topics: Code reading, testing, recursion, documentation

As you've probably noted, we like to write long procedure names that make it perfectly clear what procedures are supposed to do. These procedures have names like image-compute-pixels! or rgb-complement. We also try to choose good names for the variables and parameters in our code.

However, in the interest of concision, some programmers prefer to give their procedures and variables short names, such as f and x. Some pay little attention to formatting.

Unfortunately, such short names can obfuscate code, particularly when combined with a lack of care in formatting. For example, consider the following useful procedure.

(define w (lambda (x y) (cond [(null? y) 0] [(equal? x (car y)) (+ 1 (w x (cdr y)))] [else (w x (cdr y))])))

Make this procedure definition easier to read as follows.

First, reformat the procedure so that the indentation properly demonstrates nesting. That is, you should choose appropriate points to put in carriage returns. Remember that DrRacket will re-indent for you after you put in those returns, as long as you select the text and type Tab.

Then, figure out what the procedure does. After doing so, change the names in the procedure to clarify the roles of various things. You should certainly rename the procedure w and the parameters x and y. (As a hint, start by figuring out what type y should be.)

Finally, write introductory comments (the six P's) to explain the purpose (and the other P's) of the procedure formerly named w. Pay particular attention to the preconditions.

When you format your exam, please put the introductory comments before the revised procedure definition.

Some Questions and Answers

Here we will post answers to questions of general interest. Please check here before emailing your questions!

General Questions

Question: What is a general question?
Answer: A question that is about the exam in general, not a particular problem.

Errata

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. (And no, we don't count errors in the errata section or the question and answer sections.)

  • Typo: turtlea should be just turtle. [CK, AK, DR, ELB, others, 1 point]
  • Consistency: Different capitalization in different problems. [ELB, 1 point]
  • Consistency: In this exam, the procedure described in problem 9 was called drawing-right-neighbor. However, in the previous exam, it was called smaller-right-neighbor. [AM, 1 point]
  • Formatting: Incorrect indentation on Problem 2. [JC, 1 point]
  • Typo: Missing “a” in “Write, but do not document a procedure” in problem 3. [HZ, 1 point]
  • Some inconsistency in commas. [HZ, 0 points]
  • Typo: “section” misspelled as “swection” in the errata. [BC, 0 points]
  • Sam put the wrong due date for the prologue in the list of assignments. [Various, 0 points]

Citations

Many 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 Janet Davis, Rhys Price Jones, Samuel A. Rebelsky, John David Stone, Henry Walker, and Jerod Weinman. Many were group projects, or based upon prior examinations, so precise credit is impossible.

Some problems on this exam were inspired by conversations with our students. 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.


Samuel A. Rebelsky, rebelsky@grinnell.edu

Copyright (c) 2007-2013 Janet Davis, Samuel A. Rebelsky, and Jerod Weinman. (Selected materials are copyright by John David Stone or Henry Walker and are used with permission.)

Creative Commons License

This work is licensed under a Creative Commons Attribution 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/3.0/ or send a letter to Creative Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA.