CSC151.01 2009F Functional Problem Solving : Labs

Laboratory: Association Lists and Searching


Summary: In today's laboratory, you will experiment with association lists, structures that make it easy to look up information. You will work with two kinds of association lists: lists of color information, as described in the reading, and lists of shape descriptions, similar to the drawings we encountered earlier in the course.

Preparation

a. Make a copy of the code for this lab.

b. Review the definitions in that code to make sure you understand what procedures and values are being defined.

c. In the interactions pane, create a new 200x200 image and take note of its image number.

d. In the definitions pane, write a definition to assign the name canvas to the number of the image you created. For example, if you just created image 3, you would write

(define canvas 3)

Exercises

Exercise 1: Color Tables, Revisited

In the reading on association lists, we claimed that the lookup-color-by-name procedure worked correctly, even for the extended table that included not only color names and components, but also color attributes.

Verify that claim. That is, ensure that lookup-color-by-name will find colors in the new table and will return an appropriate special value for colors not in the table.

Exercise 2: Color Attributes

Document and write a procedure, (lookup-attributes color-name color-table), that lets you find the attributes associated with a particular color.

For example,

> (lookup-attributes "yellow" named-colors)
("rainbow" "secondary" "web-safe")
> (lookup-attributes "Oregon salmon" named-colors)
#f
> (lookup-attributes "off white" named-colors)
("bw")
> (lookup-attributes "green" named-colors)
("primary" "rainbow")

Hint: You should rely on assoc to do the searching. Your goal is primarily to extract the attributes once you've found the appropriate entry.

Hint: The goal of this procedure is similar to the goal of lookup-color-by-name. You may consider using the definition of that procedure as a model for designing this procedure.

Exercise 3: A Table of Named Shapes

Let's explore an association list that uses values other than colors.

Sometimes it is useful to think a drawing in terms of a collection of named objects. Suppose we represent each object in the drawing as a list of length 7: name, shape, color, left, top, width, height, where the name, shape, and color are strings, the shape is either "rectangle" or "ellipse", and the remaining values are reals. For example,

(define drawing
  (list (list "circ1" "ellipse" "red" 10 10 80 80)
        (list "thin" "ellipse" "blue" 10 80 300 10)
        (list "tall" "rectangle" "green" 80 5 100 2)
        (list "ys1" "rectangle" "yellow" 0 50 10 10)
        (list "ys2" "rectangle" "yellow" 0 50 20 20)
        (list "ys3" "rectangle" "yellow" 0 55 30 30)
        (list "ys4" "rectangle" "yellow" 0 60 40 40)
        (list "ys5" "rectangle" "yellow" 0 65 50 50)
        (list "ys6" "rectangle" "yellow" 0 70 60 60)
        (list "rc" "ellipse" "red" 100 100 30 30)
        (list "oc" "ellipse" "orange" 90 110 30 30)
        (list "yc" "ellipse" "yellow" 80 120 30 30)
        (list "gc" "ellipse" "green" 80 130 30 30)
        (list "bc" "ellipse" "blue" 90 140 30 30)
        (list "ic" "ellipse" "indigo" 100 150 30 30)
        (list "vc" "ellipse" "violet" 110 160 30 30)
        (list "last" "rectangle" "white" 0 0 1 1)))

Here is a procedure that you might find helpful as you use these objects.

(define image-draw-named-object!
  (lambda (image named-object)
    (let ((type (list-ref named-object 1))
          (color (list-ref named-object 2))
          (left (list-ref named-object 3))
          (top (list-ref named-object 4))
          (width (list-ref named-object 5))
          (height (list-ref named-object 6)))
      (context-set-fgcolor! color)
      (if (equal? type "ellipse")
          (image-select-ellipse! image REPLACE
                                 left top width height)
          (image-select-rectangle! image REPLACE
                                   left top width height))
      (image-fill-selection! image)
      (image-select-nothing! image))))

a. Copy the definitions of drawing and image-draw-named-object! to the definitions pane. Then, using image-draw-named-object! and assoc, write an expression that tells the GIMP to draw the object named "bc" from drawing.

b. What do you expect to have happen if you use assoc to find an object in drawing called "thingy"? Check your answer experimentally.

c. The string "ellipse" appears a lot in drawing. What do you expect to have happen if you use assoc to find an object in drawing called "ellipse"? Check your answer experimentally.

Exercise 4: Finding Objects

a. Using assoc as a helper, write a procedure, (find-object name objects), that finds an object with the given name in a list of shapes. If the object isn't found, find-object should return false.

b. Using find-object and image-draw-named-object! as helpers, write a procedure, (find-and-draw-object! image objects name), that finds an object with the given name and, if it is found, draws it in the image. If the object is not found, your procedure should raise a reasonable error.

Exercise 5: Preconditions

a. Review the sample implementation of assoc.

;;; Procedure:
;;;   assoc
;;; Parameters:
;;;   key, a Scheme value
;;;   alist, an associate list
;;; Purpose:
;;;   Find an entry with key key in alist.
;;; Produces:
;;;   entry, a Scheme value
;;; Preconditions:
;;;   No additional
;;; Postconditions:
;;;   If there is an index, i, such that
;;;     (equal? key (car (list-ref alist i)))
;;;   then entry is the first such entry
;;;   Otherwise, entry is false (#f)
(define assoc
  (lambda (key alist)
    (cond
       ; If there are no entries left in the association list,
       ; there are no entries with the given key.
       ((null? alist) 
        #f)
       ; If the key we're looking for is the key of the first
       ; entry, then use that entry.
       ((equal? key (car (car alist))) 
        (car alist))
       ; Otherwise, look in the rest of the association list.
       (else 
        (assoc key (cdr alist))))))

b. What do you think that assoc will do if it is given a list in which each element is a pair, rather than a list? For example, can we use assoc to search the following list to determine the first name of a faculty member whose last name you know?

(define math-cs-stats
  (list (cons "Walker" "Henry")
        (cons "Stone" "John")
        (cons "Rebelsky" "Sam")
        (cons "Davis" "Janet")
        (cons "Coahran" "Marge")
        (cons "Weinman" "Jerod")
        (cons "Wolf" "Royce")
        (cons "Chamberland" "Marc")
        (cons "Shuman" "Karen")
        (cons "French" "Chris")
        (cons "Romano" "David")
        (cons "Mosley" "Holly")
        (cons "Kuiper" "Shonda")
        (cons "Blanchard" "Jeffrey")
        (cons "Jonkman" "Jeffrey")
        (cons "Moore" "Tom and Emily")))

c. Confirm or refute your answers by experimentation.

d. Based on your experience, what preconditions should assoc have?

Exercise 6: Finding Multiple Objects

a. Suppose we inadvertently give two objects in the same list the same name. For example, suppose we add another entry to the drawing above of the given form:

        (list "tall" "ellipse" "grey" 190 50 150 50)

What do you expect find-object to return when it is asked to search for "tall"?

b. Check your answer experimentally.

c. Write a new procedure, (find-all-objects name objects), that all the objects with the given name. (If there are no objects with the name, it should return the empty list.)

Warning! You cannot use assoc to solve this problem. You will need to write your own recursive procedure.

Exercise 7: Identifying Shapes by Characteristic

Assume that we are representing objects and drawings as above, with a drawing consisting of a list of objects, and each object a list of length seven (name, type, color, left, top, width, height).

a. Write a procedure, (find-ellipses objects), that finds all the objects in the list that are ellipses.

b. Write a procedure, (find-objects-by-color color objects), that gets a list of objects whose color matches color.

c. Use these two procedures together to write an expression to identify all the red ellipses.

For Those with Extra Time

Extra 1: Using a Specific Database

For some problems, it seems natural to always use a specific database, rather than to pass the database as a parameter. For example, suppose we've already set up a large list of colors, such as named-colors, and don't want the programmer to have to mention it in her code, perhaps because we don't even want her to know what we've named it.

For example, consider the following procedure that converts a color name to an RGB color.

(define color-name-to-rgb
  (lambda (color-name)
    (let ((entry (assoc color-name named-colors)))
      (if entry
          (rgb-new (list-ref entry 1) (list-ref entry 2) (list-ref entry 3))
          0))))

The strategy of using a specific database in a procedure is often called hard-coding the database.

a. Using color-name-to-rgb, convert off white to an RGB color.

b. Using color-name-to-rgb, convert olive green to an RGB color.

c. Suppose that we wanted to write the converse procedure, one that given an RGB color, finds the corresponding name. Can we still hard-code the database? If so, show how. If not, explain why not.

Extra 2: Finding Color Names By Components

Write a procedure, (lookup-color-by-components r g b), that takes red, green, and blue components and finds a color which matches all three components. If you fail to find a color that matches all three components, return the empty string.

Extra 3: Finding Names By Nearby Components

Write a procedure, (lookup-nearby-colors r g b), that takes red, green, and blue components and finds a color each of whose components is within sixteen of the corresponding component. If you fail to find a color that matches all three components, return the empty string.

Extra 4: Finding Multiple Entries

a. What do you expect your procedure to return if given 255, 255, and 255 as parameters?

b. Verify your results experimentally.

c. Rewrite lookup-nearby-colors so that it returns a list of all the nearby colors, rather than just the first one. Note that if there are no nearby colors, you should return the empty list.

Extra 5: Finding Entries by Attribute

So far, we haven't used the last part of each entry, the attributes that someone has assigned to the color. Note that we can get those attributes by taking the cdr of the cdr of the cdr of the cdr of the entry.

a. Write a procedure, (lookup-colors-by-attribute attribute ctable), that returns a list of entries that contain the given attribute. For example,

> (lookup-colors-by-attribute "bw" named-colors)
(("black 0 0 0 "bw" "web-safe")
 ("blah grey" 153 153 153 "bw" "web-safe")
 ("medium grey" 128 128 128 "bw")
 ("off white" 250 250 250 "bw")
 ("white" 255 255 255 "bw" "web-safe"))

You may find the following procedure useful.

;;; Procedure:
;;;   list-contains?
;;; Parameters:
;;;   lst, a list
;;;   val, a value
;;; Purpose:
;;;   Determines if lst contains val.
;;; Produces:
;;;   contained?, a Boolean
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   If there is an i such that (list-ref lst i) equals val,
;;;     then contained? is true (#t).
;;;   Otherwise,
;;;     contained? is false.
(define list-contains?
  (lambda (lst val)
    (and (not (null? lst))
         (or (equal? (car lst) val)
             (list-contains? (cdr lst) val)))))

b. Suppose we wanted to find colors that were both rainbow colors and web safe. Describe a process for doing so. (Don't write a new procedure; preferably, you should do this with a few commands in the interactions pane.

Creative Commons License

Samuel A. Rebelsky, rebelsky@grinnell.edu

Copyright (c) 2007-9 Janet Davis, Matthew Kluber, Samuel A. Rebelsky, and Jerod Weinman. (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.