In addition to constants and atoms, Scheme allows us to express structured data; in particular, Scheme makes considerable use of lists and related structures known as S-expressions.
The syntax for lists in Scheme is as follows: we write the items in the list one after another, separated by spaces, and surround the whole thing in a pair of parentheses. For example, the following are all lists in Scheme.
The first of these examples is simply a list of numeric constants. The second one is a list which contains both numeric and boolean constants; this sort of "mixed" or heterogeneous list is perfectly acceptable in Scheme, even though it would not be allowed in many statically-typed languages. The third example list is an even more extreme example of this same phenomenon, since it includes numeric constants, boolean constants and even another (heterogeneous!) list among its contents.
As it turns out, lists in Scheme are actually built from more primitive data structures called S-expressions. As stated in The Little Schemer, an S-expression is either an atom or a collection of S-expressions enclosed in parentheses; the second form of S-expressions are known as lists. As a special case, the empty collection of S-expressions, when enclosed in parentheses, forms a value written as () in Scheme; it is also often called by old-timers (like your instructor).
> '() () > null () > () () > nil reference to undefined identifier: nil
To practice your knowledge of lists and S-expressions, read pages 3 and 4 in The Little Schemer; when you feel confident, move on to the second exercise.
( x y ) za list? Why or why not?
( x y ) zan S-expression? Why or why not?
()an S-expression? Why or why not?
Three primitive functions in Scheme are fundamental to the manipulation of lists (and S-expressions): these are , , and .
The function cons is used as a primitive way of constructing lists from their component parts. It takes an item and a list as arguments and returns a list constructed from the two of them, with the item occurring as the first item in the new list.
The function car is used as a way of taking lists apart; when applied to a list built using cons, it returns the first item in the list, i.e., what would have been the first argument to the cons function that originally built the list.
The function cdr (pronounced "could-er") is the other way of taking lists apart; when applied to a list built using cons, it returns the rest of the list, i.e., the list that would have been the second argument to the cons function that originally built the list.
By way of example, see if you can follow the reasoning behind these results reported by Scheme:
> (cons 1 ()) (1) > (cons 1 (cons 2 ())) (1 2) > (car (cons 1 (cons 2 ()))) 1 > (cdr (cons 1 (cons 2 ()))) (2)
Note that a 1-element list is written with its (single) item surrounded by parentheses, just as you would expect from the rules above. In particular, a list with one item is not the same as that item alone by itself. To see this, we can use the predicate to compare these two values; equal? is similar to in that it compares its arguments to see if they are the same. For technical reasons we will study later, it is best to use eq? for atoms and equal? in situations where one or more arguments may be lists, at least at this point in our explorations.
> (cons 1 ()) (1) > (equal? (cons 1 ()) (cons 1 ())) #t > (equal? 1 (cons 1 ())) #f
Now go and read pages 5-8 and the top half of page 9 in The Little Schemer. Make sure you understand the meaning of , , and . Use Scheme to interactively explore the use of these functions if you have any questions.