Functional Problem Solving (CSC 151 2015S) : Assignments
Primary: [Front Door] [Schedule] - [Academic Honesty] [Disabilities] [Email] - [FAQ] [Teaching & Learning] [Grading] [Taking Notes] [Rubric] - [Calendar]
Current: [Assignment] [EBoard am] [EBoard pm] [Lab] [Outline] [Reading]
Sections: [Assignments] [EBoards am] [EBoards pm] [Labs] [Outlines] [Readings] - [Examples] [Handouts]
Reference: [Setup] [VM] [Errors] - [Functions A-Z] [Functions By Topic] - [Racket] [Scheme Report (R5RS)] [R6RS] [TSPL4]
Related Courses: [Davis (2013F)] [Rebelsky (2014F)] [Weinman (2014F)]
Misc: [Submit Questions] - [SamR] [Glimmer Labs] [CS@Grinnell] [Grinnell] - [Book Office Hours] - [Issue Tracker (Course)]
Due: 10:30 p.m., 24 February 2015
Summary:
In this assignment, you will explore the use of map
to make more interesting images and to be better understandcolor
transformations.
Purposes: To give you more experience with
the map and anonymous procedures.
Collaboration: You may work individually or with your assigned partner on this assignment. You may discuss this assignment with anyone, provided you credit such discussions when you submit the assignment.
Wrapper (Prologue): Individually read through this assignment and make sure that you understand what is required. Then use the form available at http://bit.ly/151hw5pro to indicate (a) how long you think this assignment will take and (b) what you think will be the most challenging aspect of this assignment.
Wrapper (Epilogue): When you are done with the assignment, fill out the form available at http://bit.ly/151hw5epi to indicate (a) how long the assignment took, (b) what the most challenging part of the assignment was, and (c) something important you learned from doing the assignment. If you find that the assignment took much less or much more time than you expected, also include (d) a note as to what might have led to that difference.
Submitting:
Email your answer to <grader-151@cs.grinnell.edu>. The title of
your email should have the
form CSC 151.00 Assignment 5: Conditionals
and should contain your answers to all parts of the assignment.
Scheme code should be in the body of the message.
Warning: So that this assignment is a learning experience for everyone, we may spend class time publicly critiquing your work.
In the recent reading on homogeneous
lists, you learned that one straightforward way to use
map for manipulating a list of drawings with a
procedure that takes two parameters (one of them a drawing) is to build
an auxiliary list containing copies of the other parameter.
For example, say we wanted to scale every drawing by the same amount. We might write scale-drawings as follows.
(define scale-drawings
(lambda (factor drawings)
(map scale-drawing
(make-list (length drawings) factor)
drawings)))
This procedure is particularly nice because the body looks a lot
like how we would scale a single drawing,
(, with a call to
scale-drawing
factor
drawing)map inserted before
scale-drawing, a call to
make-list used to expand the scaling factor to
a list, and finally using a list of drawings rather than a single
drawing.
Unfortunately, this approach has a darker side. Much like nested
calls to image-variant inefficiently create an
intermediate image that is eventualy discarded, the procedure
scale-drawings above creates an intermediate
list. Let's look at why that is. The function we map,
scale-drawing takes two parameters, therefore
map requires two lists. In order to give
scale-drawing its requisite scale factor for
each drawing in the list of drawings, we expand the individual
factor to a list of copies of
factor. How many? As many as there are
drawings in the list.
How can we avoid this unnecessary list creation, which could be
especially cumbersome when the list of drawings is long? If the root
of the problem is that the procedure being mapped takes two
parameters, then why don't we just use a procedure that takes only
one parameter, namely the drawing to be scaled. After all, the scale
factor doesn't vary, and one common use of map
is to repeatedly apply some operation to a list of assorted values.
We can try writing our own procedure that takes just the drawing, and we might start with something like the following.
(define scale-drawings-v2
(lambda (factor drawings)
(map my-scale-drawing drawings)))
(define my-scale-drawing
(lambda (drawing)
(scale-drawing ...
We might get stuck here, because at that point we realize we need a
scale factor. If we add another parameter to
my-scale-drawing then we are right back where
we began. If we use an externally defined scale factor, that value
wouldn't necessarily be the same one given to
scale-drawings.
Fortunately, at this point we should not abandon hope, but actually
be encouraged because we're on to something. We might not be able to
solve the problem by naming a separate
my-scale-drawing procedure. However, because
factor gets its value inside the body of
scale-drawings, we could write an expression
using factor there. Moreover, because what we
need is a procedure that takes a single argument (just like
my-scale-drawing did), it seems the expression
we need is a procedure. In this case, that procedure will need to be
anonymous. Putting all of this together, we might write the following.
(define scale-drawings-v3
(lambda (factor drawings)
(map
(lambda (drawing)
(scale-drawing factor drawing))
drawings)))
What does this code do? In the body of
scale-drawings we map a procedure that takes a
single parameter, a drawing, over a list of drawings. That anonymous
procedure scales the given drawing by the
factor given to
scale-drawings.
While this solution no longer has the form analogous to a call to
scale-drawing, it does get eliminate the
inefficient building of an extra list. We will leave it to you to
decide which you prefer. (At least one instructor prefers the
efficient version more because it does not sacrifice elegance.)
What is the moral of this story? In part, it is to demonstrate that there are alternative approaches. Moreover (as you should discover in working the problems for this assignment), sometimes using an anonymous procedure this way is the only way to solve the problem.
As you saw in your initial exploration of RGB colors in GIMP and MediaScript, there are a wide range of colors possible. You may have also discovered that it is difficult to figure out what color a particular RGB triple, such as (18,223,51) represents. It is also useful to see how a variety of colors relate to each other.
It can therefore be helpful to build tools to help you understand colors and their relationships. We will start by building such a tool.
Write a procedure,
(,
that produces a simple
visualization of a list of colors by making a list of copies of some
simple shape, each colored with a different color, and each shifted
slightly from the last.
You may choose the shape, size, and amount to shift subsequent shapes.
visualize-colors
list-of-colors
number-of-colors)
For example, consider the following command
>(visualize-colors (list "red" "orange" "yellow" "green" "blue" "indigo" "violet") 7)
If we use circles of diameter 20, with each subsequent circle starting 15 units to the right of the previous circle, we should get something like the following.

Similarly, we can visualize a variety of shades that start with pink using the following.
>(define PINK (color-name->irgb "pink"))>(visualize-colors (list PINK (irgb-darker PINK) (irgb-darker (irgb-darker PINK)) (irgb-darker (irgb-darker (irgb-darker PINK))) (irgb-darker (irgb-darker (irgb-darker (irgb-darker PINK))))) 5)
Using the same visualization technique (circles of radius 20, spaced by 15 units), we would get the following image.

You will find it easier to do this assignment if you break the problem down in to steps.
map (along with an appropriate procedure)
to offset your shapes.
map (along with an appropriate procedure)
to color your shapes.
Using your visualize-colors procedure as a subroutine,
write a procedure
(, that takes as
input an integer-encoded RGB color
and a list of color transforms (along with the list's
length) and visualizes the result of applying each transform to the color.
visualize-transforms
irgb-color
list-of-transforms
number-of-transforms)
For example,
>(visualize-transforms (color->irgb "pink") (list (lambda (irgb-color) irgb-color) irgb-darker (o irgb-darker irgb-darker) (o irgb-darker irgb-darker irgb-darker) (o irgb-darker irgb-darker irgb-darker irgb-darker)) 5)
might give

Hint: If you can turn the list of transformations
into a list of colors, you can then call
visualize-colors on that list of colors.
Do not copy and paste your code from Problem 1:
This will just make your solution to this problem more complicated
and harder to understand. (Also, if you made any errors in
visualize-colors, now you will have two places to
fix that error instead of one!)
In the previous problem, you explored one way to visualize a list of transformations: You apply them all to the same color and show the results. But it's often much more interesting to see what a transformation does not just to a color, but to a whole image.
As you learned in
the reading on
transforming images, once we have a function that transforms
a color to another color, we can apply that function to each pixel
in an image with image-variant. For example,
if kitty is an image, we can make a slightly darker version
of that image with
>(define darker-kitty (image-variant kitty rgb-darker))
Often, it is useful to see what effect each of a variety of color transformations have on an image. For example, given a starting image, we might want one copy that is slightly darker, one copy that is slightly lighter, one copy that is slightly redder, and one copy that is slightly greener.
In essence, our algorithm might be expressed as follows.
For each color-transformation Use image-variant to apply that transformation to the image Use image-show to show the resulting image
Express that algorithm in Scheme in as concise a form as possible.
(define image-show-variants
(lambda (image list-of-transformations)
...))
As the narrative suggests, image-show-variants
will apply each transformation to image and then show the result.
So if there are twenty transformations, your procedures should
show twenty variations.
Note that concision is an explicit goal of this problem.
If you feel particularly energetic, you might see if you can combine all of these variations into a grid in a single image, thereby achieving a kind of Warholesque result. (This step is completely optional.)
You've seen that you can obtain the color at any position in any
image by using image-get-pixel. You've seen
that once you have a color and a position, you can make a shape
of that color at that position. We can put these two ideas together
to make a somewhat strange, but also very powerful image construction
algorithm.
Write a procedure, (, that
finds the color at each (x,y) pair, makes a simple shape of
your choice (e.g., an ellipse with width 13 and height 9) in that color
at that point, combines all of the shapes, and renders them in a
new image of the same size.
image-sample
image list-of-x-coordinates
list-of-y-coordinates)
For example, (
would sample the image called image-sample kitten
(list 10 20 40 80 160 320) (list 50 60 70 80 90 100))kitten at positions
(10,50), (20,60), (40,70), (80,80), (160,90), and (320,100).
We will judge your solutions on their correctness, conciseness, and cleverness.