Fundamentals of Computer Science I: Media Computing (CS151.01 2008S)

Higher-Order Procedures


Summary: In this laboratory, you will use and define higher-order procedures.

Preparation

a. Create a new 200x200 image called canvas.

b. Add the list of rainbow colors to your definitions pane or to your library.

;;; Value:
;;;   colors-rainbow
;;; Type:
;;;   List of colors 
;;; Contains:
;;;   A list of colors of the rainbow, suitable for processing by 
;;;   random-rainbow-color.
(define colors-rainbow
  (list color-red color-orange color-yellow color-green color-blue
        color-indigo color-violet))

c. Read the following procedure and make sure that you understand what it is intended to do.

;;; Procedure:
;;;   image-render-colors!
;;; Parameters:
;;;   image, an image
;;;   colors, a list of RGB colors
;;; Purpose:
;;;   Render a list of colors in an image.  
;;; Produces:
;;;   [Nothing; Called for the side effect]
;;; Preconditions:
;;;   colors is nonempty
;;; Postconditions:
;;;   Each color in colors has been rendered somewhere on the image.
;;; Philosophy:
;;;   Intended mostly as a technique for exploring color lists.
(define image-render-colors!
  (lambda (image colors)
    (let ((width (round (/ (image-width image) (length colors)))))
      (letrec ((kernel 
                (lambda (left remaining)
                  (cond 
                    ((null? remaining)
                     (image-select-nothing! image)
                     image)
                    (else
                     (context-set-fgcolor! (car remaining))
                     (image-select-rectangle! image selection-replace
                                              left 0 
                                              ; Select a bit too much to handle rounding effects
                                              (+ 1 width) (image-height image))
                     (image-fill! image)
                     (kernel (+ left width) (cdr remaining)))))))
        (kernel 0 colors)
        (context-update-displays!)))))

d. Add image-render-colors! to your definitions pane or to your library.

e. Add the definition of rgb-average to your definitions pane or to your library.

(define rgb-average
  (lambda (c1 c2)
    (rgb-new (quotient (+ (rgb-red c1) (rgb-red c2)) 2)
             (quotient (+ (rgb-green c1) (rgb-green c2)) 2)
             (quotient (+ (rgb-blue c1) (rgb-blue c2)) 2))))

f. Add the definition of iota to your definitions pane or to your library.

;;; Procedure:
;;;   iota
;;; Parameters:
;;;   n, an integer
;;; Purpose:
;;;   Create a list of integers between 0 and n-1, inclusive.
;;; Produces:
;;;   ints, a list of integers.
;;; Preconditions:
;;;   n >= 0
;;; Postconditions:
;;;   (length ints) = n
;;;   For all i, 0 <= i < n, (list-ref ints i) = i
(define iota
  (lambda (n)
    (let kernel ((i 0))
      (if (= i n)
          null
          (cons i (kernel (+ i 1)))))))

Exercises

Exercise 1: Using map

Recall that (map proc lst) builds a new list by applying proc to each element of lst in succession.

a. Use map and iota to compute the list of integers between 1 and 10.

b. Use map to compute the successors to the squares of the integers between 1 and 10. (That is, for each value in the list, square it and then add 1.) Your result should be the list (2 5 10 17 26 37 50 65 82 101).

c. Use map to take the last element of each list in a list of lists. The result should be a list of the last elements. For example,

> (map _____ (list (list 1 2 3) (list 4 5 6) (list 7 8 9 10) (list 11 12)))
(3 6 10 12)

d. Use apply and map to sum the last elements of each list in a list of lists of numbers. The result should be a number.

> (apply _____ (map _____ (list (list 1 2 3) (list 4 5 6) (list 7 8 9 10) (list 11 12))))
31 ; 3 + 6 + 10 + 12

Hint: You already know how to get a list of the last elements of each member list, so think about how to add them.

Reminder: As you may recall, (apply proc lst) is another way to write (proc v1 v2 ... vn) given that lst is (v1 v2 ... vn).

Exercise 2: Transforming Colors

a. What effect do you expect the following instruction to have?

> (image-render-colors! canvas (map rgb-complement colors-rainbow))

b. Check your answer experimentally.

b. Write an expression to render a darker version of the rainbow on canvas.

c. Write an expression to render a much darker version of the rainbow on canvas. (For each color, call rgb-darker three times.)

d. Here are three possible solutions to the previous problem. Which do you prefer? Why? Be prepared to discuss your reasons with the class.

> (image-render-colors! canvas
                        (map rgb-darker (map rgb-darker (map rgb-darker colors-rainbow))))
> (image-render-colors! canvas
                        (map (lambda (c) (rgb-darker (rgb-darker (rgb-darker c)))) 
                             colors-rainbow))
> (image-render-colors! canvas
                        (map (compose rgb-darker (compose rgb-darker rgb-darker))
                             colors-rainbow))

Exercise 3: Map with Multiple Lists

Although we often use the map procedure with only two parameters (a procedure and a list), it can take more than two parameters, as long as the first parameter is a procedure, the remaining parameters are lists, and the procedure can legally take the corresponding parameters from each list. For example, if the procedure is +, each list must contain numbers.

a. What colors do you expect the following expression to produce?

> (map rgb-average colors-rainbow (make-list (length colors-rainbow) color-white))

b. Check your answer experimentally, by rendering the result on canvas, by converting each color to a string, or both.

c. What do you expect the following expression to produce?

> (map rgb-average colors-rainbow (make-list 5 color-white))

d. Check your answer experimentally.

e. What do you expect the following expression to produce?

> (map rgb-average colors-rainbow (map rgb-complement colors-rainbow))

f. Check your answer experimentally.

Exercise 4: Map with Multiple Lists, Revisited

a. What do you think the value of the following expression will be?

> (map (lambda (x y) (+ x y)) (list 1 2 3) (list 4 5 6))

b. Check your answer through experimentation.

c. What do you think the value of the following expression will be?

> (map list (list 1 2 3) (list 4 5 6) (list 7 8 9))

d. Check your answer through experimentation.

e. What do you think Scheme will do when evaluating the following expression?

> (map (lambda (x y) (+ x y)) (list 1 2) (list 3 4) (list 5 6))

f. Check your answer through experimentation.

g. What do you think Scheme will do when evaluating the following expression?

> (map + (list 1 2 3) (list 4 5 6))

h. Check your answer through experimentation.

i. What do you think Scheme will do when evaluating the following expression?

> (map + (list 1 2) (list 3 4) (list 5 6))

j. Check your answer through experimentation.

Exercise 5: Dot-Product

Use apply and map to concisely define a procedure, (dot-product list1 list2), that takes as arguments two lists of numbers, equal in length, and returns the sum of the products of corresponding elements of the arguments:

> (dot-product (list 1 2 4 8) (list 11 5 7 3))
73
; ... because (1 x 11) + (2 x 5) + (4 x 7) + (8 x 3) = 11 + 10 + 28 + 24 = 73

> (dot-product null null)
0
; ... because in this case there are no products to add

Exercise 6: Tallying

a. Document and write a procedure, (list-tally lst pred?), that counts the number of values in list for which predicate holds.

b. Demonstrate the procedure by tallying the number of odd values in the list of the first twenty non-negative integers. (Note that you can use (iota 20) to create that list.)

c. Demonstrate the procedure by tallying the number of multiples of three in the list of the first twenty non-negative integers.

Exercise 7: Removing Elements

Write a procedure, (list-filter list predicate), that creates a procedure that takes a list as a parameter and removes all elements for which predicate holds.

For example,

> (define filter-whitespace (lambda (lst) (list-filter lst char-whitespace?)))
> (filter-whitespace (list #\a #\space #\b #\c))
(#\a #\b #\c)
> (list->string (filter-whitespace (string->list "Hello, my name is Dr. Fu")))
"Hello,mynameisDr.Fu"

Exercise 8: Sectioning

As you may recall, the left-section (a.k.a. l-s) and right-section (a.k.a. r-s) procedures are defined as

;;; Procedures:
;;;   left-section 
;;;   l-s
;;; Parameters:
;;;   binproc, a two-parameter procedure
;;;   left, a value
;;; Purpose:
;;;   Creates a one-parameter procedure by filling in the first parameter
;;    of binproc. 
;;; Produces:
;;;   unproc, a one-parameter procedure 
;;; Preconditions:  
;;;   left is a valid first parameter for binproc.
;;; Postconditions:
;;;   (unproc right) = (binproc left right)
(define left-section
  (lambda (binproc arg1)
    ; Build a new procedure of one argument
    (lambda (arg2)
      ; That calls binproc on the appropriate arguments
      (binproc arg1 arg2))))
(define l-s left-section)

;;; Procedures:
;;;   right-section 
;;;   r-s
;;; Parameters:
;;;   binproc, a two-parameter procedure
;;;   right, a value
;;; Purpose:
;;;   Creates a one-parameter procedure by filling in the second parameter
;;    of binproc. 
;;; Produces:
;;;   unproc, a one-parameter procedure 
;;; Preconditions:  
;;;   left is a valid first parameter for binproc.
;;; Postconditions:
;;;   (unproc left) = (binproc left right)
(define right-section
  (lambda (binproc arg2)
    ; Build a new procedure of one argument
    (lambda (arg1)
      ; That calls binproc on the appropriate arguments
      (binproc arg1 arg2))))
(define r-s right-section)

a. Using right-section or r-s, define a procedure, (filter-odds lst), that filters out all of the odd numbers from a list. Your definition is likely to involve list-filter, the odd? predicate, and perhaps a few other things.

b. Write an expression (not a procedure), that filters out all vowels from the string "The quick brown fox jumped over the lazy dog." As a hint, you may find it helpful to use string->list, list->string, and perhaps even member.

For Those With Extra Time

Extra 1: Any

Write a procedure, (any pred? lst), that holds only when lst contains at least one value for which pred? holds.

Extra 2: Vector Mapping

Write a procedure, (vector-map! proc vec), that replaces each element of vec with the result of applying proc to the original element.

Creative Commons License

Samuel A. Rebelsky, rebelsky@grinnell.edu

Copyright (c) 2007-8 Janet Davis, Matthew Kluber, and Samuel A. Rebelsky. (Selected materials copyright by John David Stone and Henry Walker and used by permission.)

This material is based upon work partially supported by the National Science Foundation under Grant No. CCLI-0633090. Any opinions, findings, and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation.

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