< 4. The Cartesian Plane | 6. Conditionals > |
5. Lists
The primary compound data structure provided by Scheme is the list. We've been using lists the whole time—they're written as a sequence of values surrounded by parentheses.
It is Scheme's syntax (rules of how code must be written) that specifies that the first element of a list should be a function that will be called. This is the behavior we've seen so far—lists as code.
To create a value that is a list with data, we use the list
function:
(list 3 5 12)
Notice what appears in the console: (3 5 12)
. This is the printed representation of the list with the elements 3
, 5
, and 12
.
If we try evaluating it, we get an error, because Scheme thinks we are trying to call the function named 3
.
(3 5 12)
Remember (define ...)
? Let's define an arbitrary short lucky-numbers
list:
(define lucky-numbers (list 7 21 32 51 55 60))
The size of a list can be obtained with length
:
(length lucky-numbers)
A shortcut for writing lists
If you're manually writing many lists, there's a shortcut called quote
that tells Scheme not to try evaluating the value following quote
. For example:
(quote ("apples" "bananas" "oranges"))
It looks like list
, but there are some differences. For now, both ways of making lists will work. A value created with quote
should not be modified, but this will not happen in this book.
So far, I've just replaced list
with quote
. The shortcut is that (quote value)
may be written as 'value
. This is a "straight" quote ('
), not a "backquote" (`
). Be careful not to mix them up.
So the code above is the same as:
'("apples" "bananas" "oranges")
quote
happens to be a special form, which use reserved words. Special forms look like functions, but behave according to special rules. If quote
were a function, the list would have had to be evaluated, and you would have gotten an error.
Generating evenly spaced numbers
The iota
function creates a list with a given number of elements, starting from a specific number and with a specific step between adjacent numbers.
The simplest way of using iota
is passing a single number. This many numbers will be created, starting from 0
. (Most modern programming languages start counting from 0
).
(iota 5)
If you add another argument, say 10
, the resulting list will start at 10
. (five numbers, starting from 10
and stepping by 1
).
(iota 5 10)
Finally, passing three numbers will specify the step as well:
(iota 5 10 30)
Iterating over a list
It would be impractical to iterate (repeatedly take an element one by one) over a long list. Of course, we wish to do something with each value. Remember that functions are actions that operate on values.
The function for-each
takes a function name (for example, f
) as the first argument, followed by lists. The number of lists you need to pass depends on how many arguments f
takes.
It sounds confusing, so let's look at some examples.
The simplest thing you can do to a value is print
it.
(define players '("Adam" "Beth" "Cathy")) (for-each print players)
Look at the output in the console. Each player's name was printed, one per line.
Let's take a look at some code that creates a drawing of concentric circles (they share the same center).
(define (centered-circle radius) (circle 0 0 radius)) (clear) (set-stroke! "SkyBlue") (for-each centered-circle (iota 4 10 50))
The first part defines the centered-circle
function, then the graphics box is clear
ed, a color is chosen, and the list of radii (10 60 110 160)
is passed to for-each
so that it knows what sized centered-circle
to draw!
Above, I've said that it matters how many arguments are accepted by the function passed to for-each
. We'll use the rectangle-drawing function, rect
, as another argument to for-each
.
Let's make all rectangles' bottom-left corner fixed at (0, 0)
. So we will only vary their widths and heights. We want to vary the width greatly, but the heights, less so:
(define (fixed-rect w h) (rect 0 0 w h)) (clear) (axes) (set-stroke! "SkyBlue") (define widths '(30 70 110 170)) (define heights '(10 15 25 35)) (for-each fixed-rect widths heights)
What is happening is that fixed-rect
operates initially on the first element of widths
and the first element of heights
. Then, it moves on to the second elements, and so on.
We can achieve the same drawing without for-each
, by drawing the four rectangles with their correct size. The color has been changed so you can compare the two versions:
(clear) (axes) (set-stroke! "Maroon") (rect 0 0 30 10) (rect 0 0 70 15) (rect 0 0 110 25) (rect 0 0 170 35)
With a few items, the amount of code is nearly the same. However, consider applying a function to all 360
pixels of the x-axis. Certainly you will want a concise manner of doing so!
< 4. The Cartesian Plane | 6. Conditionals > |