Summary: In this laboratory, you will explore not only the basic list operations, but also some applications of those operations in working with images.
Contents:
cons-fusing list-ref
Create a new 100x100 image and name it canvas.
a. Call the cons procedure to build a list of
the value
color.red. The result of your call should be
(-16776961).
b. Call the cons procedure to build a list of
the value
5 followed by the value of color.red. The
result of your
call should be (5 -16776961).
Note that you will need to call cons twice to
build this
list.
c. Call the cons procedure to build a list of
the value
2, followed by the value 5, followed by the value of color.red.
The result of your call should be
(2 5 -16776961).
Note that you will need to call cons three
times to build
this list.
d. Build the same list as in step c, using list
rather than cons.
Consider the following list:
(define numbers (list 1 2 3 4 5 6 7 8))
a. What do you expect the result of (car numbers)
to be? Check your answer experimentally.
b. What do you expect the result of (cdr numbers)
to be? Check your answer experimentally.
c. What do you expect the result of (car (cdr numbers))
to be? Check your answer experimentally.
d. What do you expect the result of (cdr (cdr numbers))
to be? Check your answer experimentally.
e. What do you expect the result of (cdr (car numbers))
to be? Check your answer experimentally.
f. Write an expression that gets the sixth element of numbers.
(That is, your expression should extract the 6.)
a. Create the list ("red" "orange" "yellow")
and name it roy. Create the list ("green"
"blue") and name it gb.
b. Determine what happens when you reverse roy
with (reverse roy).
c. Determine what happens when you append the two lists together with (append
roy gb).
d. Determine what happens when you append the two lists together with (append
gb roy).
cons-fusing
As you may recall, the cons procedure takes
two
parameters, a value and a list. It builds a new list by prepending the
value to another list. However, it is also possible to apply cons
to two non-list values. (You should not do so at this point in your
career, but some do accidentally, so we want you to see what happens.)
Consider the following:
(define one-two (cons 1 2))
(define won-too (list 1 2))
a. Execute those definitions and then ask for the values of one-two
and won-too. Explain how the two are and are
not similar.
b. What do you expect to have happen when you apply the list?
predicate to each. Check your answer experimentally.
c. What do you expect to have happen when you call reverse
on each? Check your answer experimentally.
d. What do you expect to have happen if you try to get the car and the
cdr of one-two and won-too.
Check your answer
experimentally.
e. What do you expect to have happen if you append each of these lists
to
the list (3 4), as in the following example?
(append one-two (list 3 4))
(append won-too (list 3 4))
Check your answer experimentally.
f. What do you expect to have happen if you append the list (0
0) to each of these two lists, as in the following
example?
(append (list 0 0) one-two)
(append (list 0 0) won-too)
If you are confused by any of the results, please look at the notes on this problem.
list-ref
As you may recall, in your exploration of conditionals, you used
conditionals
to draw stripes. Another technique for drawing stripes involves using
list-ref cleverly. Suppose we want five colors
of stripes.
We create a list of those five colors:
(define stripe-colors
(list (rgb.new 255 0 0)
(rgb.new 255 255 0)
(rgb.new 0 255 0)
(rgb.new 0 255 255)
(rgb.new 0 0 255)))
a. Suppose we want our stripes to be just one column wide. Then we can simply use the column (modulo five) to select the color, as in
(region.compute-pixels!
canvas 0 0 99 99
(lambda (pos) (list-ref stripe-colors (modulo (position.col pos) 5))))
Confirm that this technique works correctly.
b. Extend that code to use seven colors instead of five. That is, you'll need to add two values to the list (presumably, by redefining the list) and use a different modulus.
c. We can make the columns wider by computing the quotient of the
column
and the desired width. Try expanding your columns to a width of 5 and
then
10. (Note that in order to use the quotient in modulo
and then in list-ref, we need to guarantee
that it is an integer. Hence, you should compute the quotient with
the quotient procedure rrather than
/.)
We claimed that the reading and the lab were about representing images as lists of spots. However, up to this point, we've just worked with lists. Let's now build some procedures that work with spots. We've decided to represent each spot as a three element list, where the first element is the column, the second the row, and the third the color.
When creating a new way to represent data, we often start by writing the constructor.
Write a procedure, (spot.new col row
color)
that creates a list of the form (col row
color).
Of course, we also want to extract information from the spot.
a. Write a procedure, (spot.col spot),
that extracts
the column from a spot represented as a three-element list.
b. Write a procedure, (spot.row spot),
that
extracts the row from a spot represented as a three-element list.
c. Write a procedure, (spot.color spot),
that
extracts the color from a spot represented as a three-element list.
Of course, representing spots this way is not sufficient to draw them. To draw them, we must also put them into an image. Here is a procedure that will do so:
(define spot.draw
(lambda (spot image)
(image.set-pixel! image
(spot.col spot) (spot.row spot)
(spot.color spot))))
Here are a few spots, put into a list:
(define spots
(list (spot.new 0 0 (rgb.new 255 0 0))
(spot.new 1 0 (rgb.new 128 128 0))
(spot.new 2 0 (rgb.new 0 255 0))
(spot.new 0 1 (rgb.new 128 0 128))
(spot.new 1 1 (rgb.new 0 128 128))
(spot.new 0 2 (rgb.new 0 0 255))))
Using a sequence of calls to spot.draw and list-ref,
draw each spot on canvas.
As you've noted many times, when we set just one pixel at a time, it's
hard to see the changes. One opportunity we have is to draw bigger
spots. That is, we treat a spot
as a larger area in
an image, and
the row and column relative to the larger size, rather than to the row
and column in the image grid.
Suppose we decide that we want to represent each spot in
a list as a 10x10 grid of pixels in the image. We can use
region.compute-pixels! to set the 10x10 area
to
a single color, as in
(region.compute-pixels!
image
(* col 10) (* row 10)
(+ 9 (* col 10)) (+ 9 (* row 10) )
(lambda (pos) color))
a. Write a procedure, (spot.decadraw spot
image),
that draws a 10x10 square for the spot at the appropriate location in
the image. For example, the spot with column 5 and row 3 would be
drawn in the region from (50,30) to (59,39).
b. Test your procedure on the list of spots from the previous problem.
(spot.decadraw (list-ref spots 0) canvas)
(spot.decadraw (list-ref spots 1) canvas)
(spot.decadraw (list-ref spots 2) canvas)
(spot.decadraw (list-ref spots 3) canvas)
(spot.decadraw (list-ref spots 4) canvas)
(spot.decadraw (list-ref spots 5) canvas)
One advantage of the three-element lists representation is that we can easily compute modified versions of spots. For example, we might translate a spot horizontally or vertically. (In the next lab, we'll see how to do so for a whole list of spos.)
a. Write a procedure, (spot.htrans spot
amt),
that horizontally translates the given spot by the specified amount.
For example,
> (define woof (spot.new 2 1 color.white))
> woof
(1 1 -1)
> (spot.htrans woof 5)
(7 1 -1)
> woof
(1 1 -1)
b. Write a procedure, (spot.vtrans spot
amt),
the vertically translates the given spot by the specified amount.
(Positive amounts mean to translate down, negative amounts mean to
translate
up.) For example,
> (spot.vtrans woof -1)
(1 1 -1)
c. Test your procedures by creating a sample spot and then translating and drawing it with different offsets.
In exercise 8, you wrote a procedure that enlarged
spots by drawing each spot as a 10x10 grid. Let's generalize that
technique.
Write a procedure, (spot.scaled-draw spot
scale image), that
draws a scalexscale square for
each spot.
In exercise 5, you
explored ways to use
lists of colors and list-ref to draw stripes.
We can generalize
this technique to draw interesting
images with a
limited palette
of colors. Here's what one might do:
modulo.
Using that technique, write an expression that draws an interesting pattern. The code should look something like this:
(define colors (list ___))
(region.compute-pixels!
canvas 0 0 99 99
(lambda (pos) (list-ref colors (modulo (___) 5))))
Your creative challenges are to choose an appropriate group of colors and a formula that provides an interesting structure.
cons-fusing
As you might guess, won-two is the list (1
2).
As you might not have guessed, one-two is the
value
(1 . 2). That value looks much like a list,
but it has
a period in the middle. The period is a signal to you that the value
is not a list.
Since one-two is not a list, it is not
possible to reverse
it or to append it to another list.
However, like the typical implementation of cons,
the typical implementation of append does not
confirm that its second
parameter is a list. And, like cons, when
given a non-list
as a second parameter, append returns a
non-list. In this
case, append returns (0 0 1 . 2).
Once
again, the period indicates hey, that's not
a list
.
Why does Scheme permit these non-lists? Because they are a generalization of lists (or at least of the techniques by which we process lists). As we'll see later in the semester, these non-lists can be quite useful.
Janet Davis (davisjan@cs.grinnell.edu)
Created September 24, 2007 based on http://www.cs.grinnell.edu/~rebelsky/Courses/CS151/2007F/Labs/spot-lists-lab.html