Laboratory: Writing Your Own ProceduresSummary:
In this laboratory, you will explore some of the issues that pertain
to writing your own procedures.
Preparation
a. Review How Scheme Evaluates Expressions (version 2).
b. Make a copy of procedures-lab.scm, which contains most of the code from the reading.
c. Review the file to see what values and procedure are included.
(You may find it easiest to look at the list provided by the
Index button in MediaScript.
ExercisesExercise 1: A Procedure for Bounding
Values
Recall that in the lab on numeric
values, you explored the use of the min
and max procedures to compute a bounded value:
> (define bounded-val (min (max val lower) upper))
This code assumed that val, lower, and
upper had already been defined. It would be helpful to
create a subroutine, instead, so that we can easily limit any real number
to fall within any bounds.
Write a procedure, (boundvallowerupper), that bounds the value of
val to be between lower and
upper.
Here is an example of some output from the bound
procedure:
> (bound 5 0 10)5
> (bound 5 10 20)10
> (bound 0.7 -1 1)0.7
> (bound 1.01 -1 1)1.0
> (bound -8.3 -1 1)-1.0Exercise 2: Sanity Checks
a. Verify that the four basic values look as they are described
in the corresponding
reading.
>(image-show (drawing->image black-circle 200 100))>(image-show (drawing->image purple-ellipse 200 100))>(image-show (drawing->image blue-i 200 100))>(image-show (drawing->image red-eye 200 100))
b. Try some of the transformers. For example,
>(image-show (drawing->image (variant-1 black-circle) 200 100))>(image-show (drawing->image (variant-1 (variant-1 black-circle)) 200 100))>(image-show (drawing->image (add-right-neighbor red-eye) 200 100))
c. Try the circle constructor.
>(image-show (drawing->image (circle ___ ___ ___) 200 100))
d. Verify that
square correctly squares the
numbers 5, 10, -3, 1.2, and 0.05.
Exercise 3: Drawing Squares
Now that we can draw circles using
circle, it will be helpful to write
procedures that generate other simple shapes. Let's start with squares.
How do we create a drawing of a square? It depends on how we want
to describe the square. If we are centering the square
on a particular point, we need to know (1) the x coordinate of the
center, (2) the y coordinate of the center, and (3) the edge length
of the square. We can then scale the unit square by the edge length
and shift it horizontally by the x coordinate of the center and
vertically by the y coordinate of the center. Suppose we are drawing
a square with edge length 25, centered at (20,30). We might write
(define my-square
(drawing-hshift
(drawing-vshift
(drawing-scale drawing-unit-square 25)
30)
20))
Now, let's think about how to generalize this.
a. Write a procedure, (centered-squareedge-lengthcenter-xcenter-y)
that creates a drawing of a square. (We couldn't call this procedure
square, because we'd already used
that name for the procedure that squares numbers.) You should be
able to generalize the code above, and base your procedure on the
circle procedure.
b. However, most of us don't like to draw our squares centered on a
particular point. We'd rather specify the left edge and the top edge.
How can we do that? Suppose we want to draw a square of side-length
30, with the left edge at 17 and the top edge at 42. We first scale
the unit square by 30. That means that the left edge, which was
at -1/2, is now at -15 (that is, -1/2 * 30). The top edge, similarly,
is also at -15. To get the left edge at 17, we need to shift it
horizontally by 32 (that is, 15 + 17). To get the top edge at 42, we
need to shift it vertically by 59 (that is, 15 + 42).
In Scheme, we might write
(define another-square
(drawing-hshift
(drawing-vshift
(drawing-scale drawing-unit-square 30)
(+ (/ 30 2) 42))
(+ (/ 30 2) 17)))
Of course, we can also generalize this code.
Write a procedure (drawing-of-squareedge-lengthlefttop) that creates a drawing of a square of the
specified edge length, with the left side of the square at
left and the top side at top.
Exercise 4: Drawing Circles, Revisited
One deficiency of the circle procedure
from the reading and of the centered-square and
drawing-of-square procedures you've just written
is that they don't allow you to specify the color of
the circle or square.
Rewrite the circle procedure to
take a color as a parameter. For example,
(circle 20 10 10 "blue")Exercise 5: Drawing Rectangles
Write a procedure, rectangle
that creates a drawing of a rectangle. You should do your
best to figure out appropriate parameters. Here is a sample call, using
the parameters we find most natural.
(rectangle 20 10 50 80 "yellow")
If you'd like to know what we thought were appropriate parameters, you
can look at the notes on
this exercise.
Exercise 6: Fun with Neighbors
As you may recall, the add-right-neighbor
procedure makes a copy of a drawing, places the copy immediately
to the right of the original drawing, and combines the two into a
new drawing.
a. Write a procedure,
(add-smaller-right-neighbordrawing) that combines a drawing with
a duplicate neighbor that is 75% of the size of the original drawing.
(add-smaller-right-neighbor red-eye)
As you write this procedure, you will find it useful to examine the
code for add-right-neighbor
b. Write a procedure, add-nearer-right-neighbordrawing)
that combines a drawing with a duplicate neighbor of the same size,
but that overlaps 20%
(so that the left end of the neighbor is 20% left of the right end
of the original).
(add-nearer-right-neighbor red-eye)Exercise 7: Combining Transformations
We now have procedures that pair an image with a smaller right neighbor
and a nearer right neighbor. What happens if we combine these
transformations? For each of the following, predict what the image
will look like and then render it to check your prediction.
a. (add-smaller-right-neighbor (add-nearer-right-neighbor red-eye))
b. (add-nearer-right-neighbor (add-smaller-right-neighbor red-eye))
c. (add-smaller-right-neighbor (add-smaller-right-neighbor red-eye))For Those With Extra Time
If you have extra time, you may find it useful to do any of the
following exercises. (You need not do them in order.) You may also
choose to do one of the explorations.
Extra 1: Rendering Drawings
For each of the procedures above, you've likely tested your procedure
by applying it to some drawing, rendering that drawing to some image,
and then showing the image. For example,
>(image-show (drawing->image (rectangle 20 10 50 80) 200 100))
Write a procedure, (check-drawingdrawing) that renders the drawing on
a "large enough" image, then shows the image. Once we've written
that procedure, we can more easily check drawings, as in the
following.
>(check-drawing (rectangle 20 10 50 80))
How can we find out how large the image needs to be? We can
ask a drawing where its lower right hand corner is using the
drawing-bottom and
drawing-right procedures.
Extra 2: Drawing Eyes
In the reading, we defined a drawing that looks a bit like a red eye.
Write your own procedure, drawing-eye, that
builds a drawing of an eye, based on the values of its parameters.
What parameters should it have? Certainly, the color of the iris.
However, you might find other parameters useful, too.
Extra 3: An Alternate Scaling Mechanism
As you may recall, one potentially confusing aspect of the
drawing-scale procedure is that it not
only scales the drawing, it also scales the distance of the drawing
from the top-left corner.
Write a procedure, (alternate-scaledrawing), that scales a drawing, but
does not move the drawing. That is, the scaled drawing has the
same left edge and the same top edge as the original drawing.
If you're not sure how to approach this problem, you may want
to read
the notes on this
exercise.
Extra 4: Grids of Images
a. Write a procedure, (two-by-twodrawing) that creates a compound
drawing with four copies of drawing (one shifted right, one
shifted down, and one shifted down and right).
b. Write a procedure that takes the result of
two-by-two and scales it by 50%, so that
the resulting drawing is the same width and height as the original.
c. Using this new procedure, build a four-by-four grid of
one of the original images, or one of your own choosing.
Explorations
For each of these explorations, you may find the following drawing an
appropriate starting point. You might also start with a house or other
drawing you've designed. You might even start with a simple shape.
Exploration 1: Multiple Neighbors
In Extra 4, each of the expressions takes one drawing and turn it into
four drawings. Explore other ways to use this kind of replication
to build an interesting image. (You might also think about ways to
create eight, sixteen, or even more copies of the image.)
Exploration 2: More Interesting Neighbors
In Exercise 6, you developed a number of procedures that pair an
image with a neighbor. Create a few variants of those procedures that
change the neighbor in an interesting way. For example,
you might scale it differently horizontally and vertically, you might
have it overlap the original figure, you might shift it both
horizontally and vertically.
Notes on the ExercisesNotes on Exercise 5: Drawing Rectangles
We would recommend that your procedure have the form
(define rectangle
(lambda (left top width height color)
..._))
Return to the exercise.
Notes on Extra 3: An Alternate Scaling Mechanism
The alternate scaling algorithm is fairly straightforward:
Shift the drawing back to the origin. (The top and left
should both be 0.)Scale the shifted drawing.Shift the scaled drawing back to the original place. (The
top and left should match the top and left of the original
drawing.)
Return to the exercise.