Lists and Repetition


Due: 9 a.m. Wednesday 4 March 2009

Summary: A potpourri of problems involving repetition over lists of colors.

Purposes: To practice iteration over lists using map. To practice writing recursive procedures. To see a cool technique for creating images that combines lists with image-compute.

Expected Time: Two to three hours.

Collaboration: We encourage you to work in groups of size three. You may, however, work alone or work in a group of size two or size four. You may discuss this assignment with anyone, provided you credit such discussions when you submit the assignment.

Submitting: Email your answer to . The title of your email should have the form HW6 and should contain your answers to all parts of the assignment. Scheme code should be in the body of the message.

Warning: So that this assignment is a learning experience for everyone, we may spend class time publicly critiquing your work.

Assignment

Problem 1: Fun with Stripes

Consider the following instructions.

(define stripe-colors
  (list (rgb-new 255 0 0) 
        (rgb-new 255 255 0) 
        (rgb-new 0 255 0) 
        (rgb-new 0 255 255)
        (rgb-new 0 0 255)))
(image-compute
 (lambda (col row) (list-ref stripe-colors (modulo col 5)))
 100 100)

a. What do you expect these instructions to do?

b. Check your answer experimentally.

c. Extend that code to use seven colors instead of five. That is, you'll need to add two values to the list (presumably, by redefining the list) and use a different modulus.

d. As you should have observed, this makes a sequence of vertical stripes, each of width one pixel. We can make the stripes wider by computing the quotient of the column and the desired width. Try expanding your columns to a width of 5 and then 10. (Note that in order to use the result as a parameter to modulo and then in list-ref, we need to guarantee that it is an integer. Hence, you should compute the quotient with quotient rather than /.)

e. Suppose that we wanted to fill the image with exactly one stripe of each color. How many stripes would there be? How wide would they be? Modify the code so that there is exactly one stripe of each color. (They might not be exactly the same width.)

f. Predict the result of the following instructions, and then try executing them.

(define canvas (image-show (image-new 200 200)))
(region-compute-pixels!
 canvas 
 100 0 100 100
 (lambda (col row) 
   (list-ref stripe-colors (modulo (quotient col 10) 5))))
(region-compute-pixels!
 canvas 
 0 100 100 100
 (lambda (col row) 
   (list-ref stripe-colors (modulo (quotient row 10) 5))))
(region-compute-pixels!
 canvas
 100 100 100 100
 (lambda (col row) 
   (list-ref stripe-colors 
             (modulo (quotient (+ row col) 10) 5))))

Visualizing palettes

Similar to an artist's palette, in digital graphics a palette is a list of colors to be used in an image. In this problem you will use the techniques you experimented with above to visualize palettes and the results of applying color transformations to those palettes.

a. Using the techniques you learned above, implement the procedure (image-palette colors width height). This procedure should return a new image of the specified width and height, created using image-compute, that has one vertical stripe of each color.

For example,

> (define stripes (map color-name->rgb (list "red" "orange" "yellow" "green" "blue" "indigo" "violet")))
> (image-show (image-palette stripes 140 100))
      

b. Write an expression using image-palette and map that creates an image showing the complements of a list of colors.

c. Implement the procedure (image-palette-comparison transform colors width height). The image-palette-comparison procedure should be similar to the image-palette procedure from above. However, the new parameter, the transform procedure, should be an RGB transformation, that is, a procedure that takes an RGB color and produces a new RGB color. The top half of the resulting image should show the original colors and the bottom half should show the transformed colors.

Your procedure should use map to apply the given transformation to all the colors in the list, and then name the resulting list using let.

For example,

> (define stripes (map color-name->rgb (list "red" "orange" "yellow" "green" "blue" "indigo" "violet")))
> (image-show (image-palette-comparison rgb-complement stripes 140 100))
      
> (define stripes (map color-name->rgb (list "red" "orange" "yellow" "green" "blue" "indigo" "violet")))
> (image-show (image-palette-comparison (o rgb-darker rgb-darker) stripes 140 100))
      

Problem 3: The darkest color

Write a procedure, (rgb-darkest colors), that, given a nonempty list of colors, finds the darkest of those colors.

You will need the definition of rgb-brightness; you may also find rgb-darker-of-two useful.

;;; 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)))))

;;; Procedure:
;;;   rgb-darker-of-two
;;; Parameters:
;;;   color1, an RGB color.
;;;   color2, an RGB color.
;;; Purpose:
;;;   Find the darker of color1 and color2.
;;; Produces:
;;;   darker, an RGB color.
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   darker is either color1 or color2
;;;   (rgb-brightness darker) <= (rgb-brightness color1)
;;;   (rgb-brightness darker) <= (rgb-brightness color2)
(define rgb-darker-of-two
  (lambda (color1 color2)
    (if (< (rgb-brightness color1) (rgb-brightness color2))
        color1
        color2)))

Problem 4: Summing multiple components, simultaneously

Note: This problem appears as Extra 1 in the lab on helper recursion. You should finish that lab before attempting this problem!

Although we've primarily used helpers to keep track of one intermediate result, we can certainly pass along more than one intermediate result. For example, in averaging a list of colors, we can keep track of the sum of reds, the sum of greens, the sum of blues, and the count of colors. In the end, we can build a new color from these computed values.

(define rgb-list-average
  (lambda (colors)
    (rgb-list-average-helper 0 0 0 0 colors)))
(define rgb-list-average-helper
  (lambda (red-so-far green-so-far blue-so-far count remaining-colors)
    (if (null? remaining-colors)
        (rgb-new (/ red-so-far count)
                 (/ green-so-far count)
                 (/ blue-so-far count))
        (rgb-list-average-helper ...))))

a. Fill in the remaining code in the recursive call.

b. Test this code to average white and black.

(rgb->string (rgb-list-average (list (rgb-new 255 255 255) (rgb-new 0 0 0))))

c. Test this code on a few other colors of your choice.

d. What do you expect to have happen if you provide rgb-list-average with the empty list?

e. Check your answer experimentally.

Problem 5: Members only

The lab on list recursion, revisited asks you to implement a procedure called member?. This is a very important procedure, and we want to make sure everyone has a chance to implement it.

Give your definition of that procedure here, crediting your lab partner(s) if appropriate.

Problem 6: Averaging adjacent colors

Write a procedure, (rgb-averages colors), that, given a list of colors, computes a new list of colors, by averaging subsequent pairs of colors. For example, if the input list is the standard seven rainbow colors (red, orange, yellow, green, blue, indigo, and violet), the output list will consist of a red-orange average, an orange-yellow average, a yellow-green average, a green-blue average, a blue-indigo average, and an indigo-violet average.

Note that the length of the result list is one less than the length of the input list.

Hint: Use the rgb-average procedure, which averages two RGB colors, to help do the work.

Hint: Although this problem appears as an extra in the basic recusion lab, you will find it easier after you complete the lab on list recursion, revisited. You will need to use a special base case to stop the recursion when there are too few colors in the list to produce an average.

Important Evaluation Criteria

We will consider the correctness, clarity, and conciseness of your code.


Jerod Weinman