Functional Problem Solving (CSC 151 2016S) : Labs
Primary: [Front Door] [Schedule] - [Academic Honesty] [Disabilities] [Email] - [FAQ] [Teaching & Learning] [Grading] [Taking Notes] [Rubric]
Current: [Assignment] [EBoard] [Lab] [Outline] [Reading]
Sections: [Assignments] [EBoards] [Labs] [Outlines] [Readings] - [Examples] [Handouts]
Reference: [Setup] [Remote] [VM] [Errors] - [Functions A-Z] [Functions By Topic] - [Racket] [Scheme Report (R5RS)] [R6RS] [TSPL4]
Related Courses: [Curtsinger (2016S)] [Davis (2013F)] [Rebelsky (2015F)] [Weinman (2014F)]
Misc: [SamR] [Glimmer Labs] [CS@Grinnell] [Grinnell] - [Issue Tracker (Course)]
Summary:
In this laboratory, you will explore some of the issues that pertain
to writing your own procedures using lambda.
a. Review How Scheme Evaluates Expressions (Take 2).
b. Start GIMP and enable the DBUS Server. Then start DrRacket.
c. Save a copy of procedures-rgb-lab.rkt, which contains most of the code from the reading.
d. Open the file in DrRacket. Review the file to see what values and procedures are included. (You may find it easiest to look at the list provided by using the DrRacket menu.) Finally, click .
If things go wrong, see our instructions for running the CSC 151 software. Ask for help if you need it.
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.
Take the code given above and encapsulate it in a procedure named
bound. This procedure should take three
parameters: val,
lower, and upper. (It
should take them in that order.) As a
reminder, the general form of a procedure definition is
(define procedure-name (lambda (formal-parameters) body))
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.0
In the reading, we saw two procedures for rotating the three components of a color. Let's consider some alternate ways to switch the components.
a. Write a procedure, (, that swaps the red and blue
components of irgb-swap-red-blue
color), which is
an integer-encoded RGB color.
color
b. Write a procedure, (, that swaps the red and green
components of irgb-swap-red-green
color)color.
c. Write a procedure that behaves like irgb-rotate by
appropriately composing
and irgb-swap-red-blue.
irgb-swap-red-green
(define new-irgb-rotate (compose ... ...))
c. Write a procedure that swaps the green and blue components by
appropriately composing
and irgb-swap-red-blue.
irgb-swap-red-green
(define irgb-swap-green-blue (compose ...))
As you likely discovered in the self-check, the
procedure does a
reasonable job, but the result doesn't seem quite right. That's because
humans tend to perceive red, green, and blue as having different
brightnesses. Hence, averaging the three does not
work as one might hope. The literature (e.g., Wikipedia)
suggests that we multiply the red component by about 0.299, the green
by about 0.587, and the blue by about .114 and then add them all up.
(More recently, there's been a recommendation to use 0.2126 for red,
0.7152 for green, and 0.0722 for blue.) We call this value the
luma.
irgb-greyscale-3
a. Write a procedure, (, that takes an integer-encoded
RGB color as a parameter and returns the luma of that color using
the appropriate formula.
irgb-luma
color)
b. Using and
irgb-luma, write a new
version of irgb-grey which
you should call irgb-greyscale.
my-irgb-greyscale
c. Try the two greyscale functions with image-variant and an
image of your choice to determine if you can observe a difference.
Do you have a responsibility to document the source of the formula
you used in your computation of irgb-luma? Why or why not?
In the reading, we started with a set of instructions for making a greyscale value.
>(define color (irgb ...))>(define grey-component (* 1/3 (+ (irgb-red color) (irgb-green color) (irgb-blue color))>(define grey-color (irgb grey-component grey-component grey-component))
We then turned that code into a series of procedures. In effect, we made something that others might consider a “derivative work” of the original. If you did the same revision, you would have a responsibility to consider whether or not to cite the original.
a. Suppose that you decided to cite the original. Write a comment that gives appropriate details. (The original comes from the reading, which was created by Charlie Curtsinger, Janet Davis, Samuel A. Rebelsky, and Jerod Weinman.)
b. Suppose you wrote the code supplied with this lab with another
student, perhaps for a previous lab. In writing the
my-irgb-greyscale procedure, which uses, but does not
change irgb-grey, do you have a responsibility to
cite that partner? Why or why not?
c. Suppose you wrote the code for irgb-grey alone,
but wrote my-irgb-greyscale with another student. Should
you cite the code for irgb-grey? Why or why not?
a. Write a procedure, (,
that recomputes each parameter as a weighted
average of itself and the two other parameters. You will compute
the weighted average by multiplying the component by the weight,
adding the two other components, and then dividing by the weight plus
two.
irgb-weighted-average
weight color)
For example, if the weight is 4, the red component is 0, the green component is 40, and the blue component is 200, the weighted average color would have
In, instead, the weight were 8 and the components were the same, the weighed average color would have
b. Try applying your procedure to the kitten image using various weights. For example, to use a weight of 8, you would write the following.
>(image-show (image-variant kitten (section irgb-weighted-average 8 <>)))
In the self-check for the corresponding reading, you were asked to write a procedure that computes the multiplicative inverse of a number. Here are two ways to write that procedure.
(define invert-a
(lambda (val)
(/ 1 val)))
(define invert-b (section / 1 <>))
a. In which cases, if any, do the two procedures behave differently?
b. Which do you prefer. Why?
When writing procedures, we may often find it helpful to write additional
procedures to help as accomplish our task.
You saw one example of that approach in some of the
procedure that
appears in the
reading. Here's another. Suppose we are computing a strange
color transformation: the red component of the result will be the average
of the red and green components of the original; the green component will
be the average of the green and blue components; and the blue component
will be the average of the blue and red components. Here's one approach
to writing that procedure.
irgb-greyscale
(define irgb-strange-average-1
(lambda (color)
(irgb (* 1/2 (+ (irgb-red color) (irgb-green color)))
(* 1/2 (+ (irgb-green color) (irgb-blue color)))
(* 1/2 (+ (irgb-blue color) (irgb-red color))))))
Here's a second approach.
(define irgb-strange-average-2
(lambda (color)
(rgb-strange-average-2 (irgb-red color)
(irgb-green color)
(irgb-blue color))))
; (rgb-strange-average-2 red green blue)
; Build an integer-encoded RGB color the uses the "strange average"
; technique to build the components.
(define rgb-strange-average-2
(lambda (red green blue)
(irgb (* 1/2 (+ red green)) (* 1/2 (+ green blue)) (* 1/2 (+ blue red)))))
Here's a third approach.
(define irgb-strange-average-3
(lambda (color)
(rgb-strange-average-3 (irgb-red color)
(irgb-green color)
(irgb-blue color))))
(define rgb-strange-average-3
(lambda (red green blue)
(irgb (ave2 red green) (ave2 green blue) (ave2 blue red))))
; (ave2 v1 v2)
; Compute the arithmetic average of v1 and v2
(define ave2
(lambda (v1 v2)
(* 1/2 (+ v1 v2))))
a. What do you see as the relative advantages of each approach?
b. The use of in rewriting
ave2 “feels”
a bit different than the use of
rgb-strange-average in rewriting
rgb-strange-average. Explain what
purposes each kind of helper seems to serve.
irgb-strange-average
If you have extra time, you may find it useful to do any of the following extra problems, which emphasize Scheme issues, or the explorations, which emphasize creative work. (You need not do them in order.)
Write a procedure, (, that takes one parameter,
an integer-encoded RGB color and turns each parameter to 255 if it is
at least 128 and to 0 if it is less than 128.
irgb-extreme
color)
>(irgb->string (irgb-extreme (0 64 200)))"0/0/255">(irgb->string (irgb-extreme (128 130 0)))"255/255/0"
Hint: A clever combination of division, rounding
(perhaps with floor), and multiplication can help you achieve
this goal.
Write a procedure, (, that takes one parameter,
an integer-encoded RGB color, and produces a new color in which
each component is 255 if it is the largest component (or tied for
largest) and 0 otherwise.
irgb-dominant
color)
>(irgb->string (irgb-dominant (irgb 0 5 0)))"0/255/0">(irgb->string (irgb-dominant (irgb 200 199 199)))"255/0/0">(irgb->string (irgb-dominant (irgb 10 0 10)))"255/0/255"
As you've discovered, irgb-weighted-average keeps components
closer to the original with larger weights, and closer to the average with
lower weights.
Predict what happens if you use weights of 1/2, 0, -1/2, and -1. Then try making variants of your image with each of those weights.