Laboratory: ConditionalsSummary:
In this laboratory, you will explore three of Scheme's primary
conditional control operations, if,
when, and cond.
Reference
If expressions typically have the form
(iftestconsequentalternative)
The Scheme interpreter first evaluates the test. If the value of the
test is false (that is, #f), the interpreter evaluates the
alternative and returns the value. If the value of the test is truish
(that is, anything that is not false), the interpreter evaluates the
consequent and returns its value.
When expressions typically have the form
(whentestexp1
...
expn)
The Scheme interpreter first evaluates the test. If the value of the
test is false (that is, #f), the interpreter ignores
the rest of the expression. If the value of the test is truish,
the interpreter evaluates the expressions in turn.
Cond expressions typically have the form
(cond
(test0exp0)
(test1exp1)
...
(testnexpn)
(else alternative))
Scheme evaluates each test in turn until one is truish. It then
evaluates the corresponding expression and returns its value. If none
of the tests is truish, then it evaluates the alternative and returns
its value.
Preparation
a. Add the following to your definitions pane to make it easy to
create and show a variety of sample images. (Make sure to replace
username with your user name.)
(load "/home/username/Desktop/library.scm")
(define grid (image-show (image-new 3 3)))
(define canvas (image-show (image-new 200 200)))
(define picture
(image-show (image-load "/home/rebelsky/MediaScheme/Images/kitten.png")))
(image-set-pixel! grid 0 0 (rgb-new 192 0 0))
(image-set-pixel! grid 1 0 (rgb-new 192 0 192))
(image-set-pixel! grid 2 0 (rgb-new 0 0 192))
(image-set-pixel! grid 0 1 (rgb-new 192 192 0))
(image-set-pixel! grid 1 1 (rgb-new 0 192 192))
(image-set-pixel! grid 2 1 (rgb-new 255 255 255))
(image-set-pixel! grid 0 2 (rgb-new 0 192 0))
(image-set-pixel! grid 1 2 (rgb-new 0 0 0))
(image-set-pixel! grid 2 2 (rgb-new 192 192 192))
b. Save your definitions pane as conditionals-lab.scm.
c. Open the
reference page for drawings in a separate window or tab.
ExercisesExercise 1: Comparing Brightness, Revisited
Recently, we've considered a number of activities that
required us to work with the brightness of colors. We typically compute
brightness using an expression which can be encapsulated in a procedure
as follows:
a. Add rgb-brightness to your library and then
reload your library.
b. Using rgb-brightness as a helper, write a procedure,
(rgb-brighter?color1color2), that returns true if
color1 is brighter than color2
and false otherwise.
c. Using rgb-brightness or
rgb-brighter? as a helper, write a procedure,
(rgb-brighter-2color1color2), that returns the brighter of
color1 and color2.
d. What do you expect the following to accomplish?
>(image-transform! grid rgb-brighter-2)
e. Check your answer experimentally.
f. As you may have noted, the transformation was invalid because
rgb-brighter-2 expects two colors, not one. Let's
try an appropriate call to rgb-brighter-2. What do
you expect the following to accomplish?
>(image-transform! grid (r-s rgb-brighter-2 (rgb-new 255 0 0)))
g. Check your answer experimentally. You may want to zoom in on
grid first, so that the changes are clearer.
f. Add rgb-brighter? and
rgb-brighter-2 to your library.
Exercise 2: Shades of Grey
Consider the following four colors:
(define grey0 (rgb-new 0 0 0))
(define grey1 (rgb-new 96 96 96))
(define grey2 (rgb-new 192 192 192))
(define grey3 (rgb-new 255 255 255))
a. Using if expressions (not
cond expressions), write a
procedure, (rgb-4greycolor), that returns
grey0, if the brightness of color is less than 25;
grey1, if the brightness of color is at least 25 but less than 50;
grey2, if the brightness of color is at least 50 but less than 75; or
grey3, if the brightness of color is at least 75.
b. Build and show a variant of grid using
rgb-4grey. (Testing our procedure on a small
image is a good way to make sure that there are no glaring errors.)
c. Build and show a variant of picture using
rgb-4grey.
d. Rewrite rgb-4grey so that it uses
cond rather than if.
When you are done with this exercise, you may want to look at
the notes on this
exercise.
Exercise 3: Safer Selections
a. What do you expect the following expressions to accomplish?
>(image-select-ellipse! canvas REPLACE -50 0 40 40)>(image-select-rectangle! canvas REPLACE 10 220 50 50)
b. Check your answer experimentally.
c. As you should have observed, both of those expressions generate an
error because the selection is outside the bounds of the image.
Write a new predicate, (image-contains-selection?imglefttopwidthheight),
that determines whether the given selection is likely to be within
img.
Hint: One case in which a selection is invalid is
when the left edge of the selection is greater than or equal to the
width of the image.
Hint: Another case in which a selection is invalid
is when the right edge (given by adding the width to the left edge)
is less than 0.
d. Now that you have a mechanism for determining
the likely safety of selections, write a new procedure
(image-safely-select-ellipse!imgoperationlefttopwidthheight)
that checks whether the selection is likely to be within the
bounds of the image and, if so, does the selection using
image-select-ellipse!. If the
selection is unlikely to be within the bounds of the image,
image-safely-select-ellipse! should select nothing.
Hint: You may want to use
when to make your decision.
Exercise 4: Rendering Drawings
Here is the drawing-render! procedure from
the reading.
And here are two sample drawings.
(define d1
(drawing-hshift
(drawing-vshift
(drawing-scale
(drawing-recolor
drawing-unit-square
"red")
50)
40)
30)
)
(define d2
(drawing-vscale
(drawing-hscale
(drawing-recolor
drawing-unit-circle
"green")
60)
120)
)
a. Test drawing-render! to verify that it does,
in fact, render both drawings.
b. As you may have observed, we don't immediately see the rendered
drawing. Update drawing-render! so that, after
rendering, it updates the display and selects nothing.
c. Extend drawing-render! so that it outlines
the drawing in black. (Recall that you can stroke
the drawing with image-stroke! to outline the
image.)
d. Extend drawing-render! so that it outlines
the drawing in black if the drawing is a light color and outlines
the drawing in grey if the drawing is a dark color. (You can choose
your own metric for dark and light.)
For Those With Extra TimeThose with extra time may choose to do the extra problems,
which focus on programming tasks, or the explorations, which focus
on the application of procedures to image creation.Extra 1: Shades of Grey, Revisited
As you may recall from the reading on
Boolean values and predicate procedures, it is possible
to use and and or
to achieve conditional behavior. The subsequent reading on
conditionals provides some additional information on the use
of and and or.
Using those techniques, rewrite rgb-4grey so that
it uses neither if nor cond.
That is, you'll need to find ways to express the same outcome using
and and or.
Extra 2: Safely Rendering Drawings
One problem with drawing-render! is that it
causes an error if we try to render a drawing outside of the image.
For example,
>(define canvas (image-new 10 10))canvas>(drawing-render! (drawing-hshift drawing-unit-circle -2) canvas)Error: image-select-ellipse!: selection is outside of the bounds of the image >(drawing-render! (drawing-hshift drawing-unit-circle 11) canvas)Error: image-select-ellipse!: selection is outside of the bounds of the image
Rewrite drawing-render! to that it only renders
drawings that fall within the bounds of the canvas.
Extra 3: Preserving Context
Consider the following procedure that draws a simple logo in a color
and position of the user's choice.
a. Make sure that you understand what the procedure does by rendering a few
copies of the logo on canvas.
b. As you may have observed, this procedure tries to preserve
the context by grabbing the color and brush before changing them, and
restoring them to the old values after it has changed them. While this
strategy works fine if we're only drawing one or two logos, it can add
unnecessary work if we are drawing a lot of logos in the same color.
Rewrite simple-logo! so that it only restores the
brush and color if they are different from the brush and color used to
create the logo. Consider using when to make
decisions about whether or not to set and restore the brush and color.
c. If we don't care about the current brush and color (or if we are
changing them a lot), we may want to give a hint to the programmer that
she need not restore the brush and color. MediaScheme includes three
procedures that do just that.
(context-preserve?) checks
whether or not procedures are encouraged to preserve the
context.(context-preserve-off!) makes
context-preserve? return false
(#f).(context-preserve-on!) makes
context-preserve? return true
(#t).
Rewrite simple-logo! so that it only restores the
color and brush if context-preserve? returns
true.
Extra 4: Safer Selections, Revisited
In exercise 3, you wrote a procedure,
image-safely-select-ellipse! that first checked
whether the selection was in the image and, if not, simply avoided
the selection operation. However, the choice to ignore the selection
may not always be appropriate. For example, if the new selection is
to replace the prior selection, we should probably select nothing.
In contrast, if the new selection is to be subtracted from the current
selection, we should maintain the current selection.
Rewrite image-safely-select-ellipse! to do what
you consider appropriate for each of the five cases:
The selection is valid (in which case, you can just use image-select-ellipse!)The selection is not valid, and the type is REPLACEThe selection is not valid, and the type is ADDThe selection is not valid, and the type is SUBTRACTThe selection is not valid, and the type is INTERSECTExplorationsExplorations are intended for students interested in further
exploring the design aspects of these techniques. They also provide
students who finish early with extra activities that may challenge
them in different ways.Exploration 1: Rendering, Revisited
You've already noted that
drawing-render! can do more than simply select an
area and then fill it. For example, we can stroke the drawing after
filling it. (We could even stroke it multiple times.)
Write a procedure, drawing-render-excessively!,
that renders a drawing in a more interesting manner. You might, for
example, stroke the drawing with a large brush, a medium brush, and
a small brush, each of different colors. You might draw a contrasting
smaller shape within the object. (E.g., within a square of edge length
d, you can draw a circle of diameter d; within a circle of diameter d,
you can draw a square of edge length (/ d (sqrt 2).)
Notes on the ExercisesNotes on Exercise 2: Shades of Grey
The if-based solution will look something like the following.
(define rgb-4grey
(lambda (color)
(if (< (rgb-brightness color) 25)
grey0
(if (< (rgb-brightness color) 50)
grey1
(if (< (rgb-brightness color) 75)
grey2
grey3)))))
The cond-based solution will look something like the
following.
(define rgb-4grey
(lambda (color)
(cond
((< (rgb-brightness color) 25)
grey0)
((< (rgb-brightness color) 50)
grey1)
((< (rgb-brightness color) 75)
grey2)
(else
grey3))))
Return to the problem.