• No results found

3.1 An Interactive Programming Environment (Hunk B)

3.1.10 Making Some Objects (Hunk D)

Hunk D starts here:

==================================================================

I've been talking about \objects," but most of the objects we've seen don't have interesting structure.

One of the most important kinds object in Scheme is the pair, which you can create with the built-in procedure cons. A pair is a simple kind of structured object, like a Pascal record or a

C struct. It has two elds, called the car and the cdr, and you can extract their values with the procedurescar andcdr.

cons takes two arguments, which it uses as the initial values of the car and cdr elds of the

pair it creates. (consis called that because it constructs a pair the name is short because it's a

common operation. In Lisp, pairs are called \cons cells" because you make them withcons.)

I'll show you some simple examples of playing with pairs, just to show you what they are. Be warned that these are bad examples, in that there are usually cleaner ways to do things, which we'll discuss later when we get to lists. (Lists are made of pairs.)

Scheme>(cons 1 2) (1 . 2)

What happened here was that the call to cons created a pair, and returned (a pointer to) it.

Scheme printed out a textual representation of the pair, showing the values of its car and cdr elds. We didn't do anything with the pair except let Scheme print it, so we've lost it|we didn't save a pointer to it, so we can't refer to it. (The garbage collector will take back its space, so we don't have to worry that we've lost storage space.)

Let's try again, dening (and binding) a variable, and initializing it with the pointer that cons

returns.

Scheme>(define my-pair (cons 1 2)) #void

Scheme>my-pair (1 . 2)

Now try extracting the values of the pair's elds, using car and cdr. (In Scheme, (car foo)

is equivalent to C'sfoo->car, dereferencing a pointer to an object and extracting the value of the careld. Likewise ,(cdr foo)is like foo->cdr. The operators that access elds of a pair are just

procedures.)

Scheme>(car my-pair) 1

Scheme>(cdr my-pair) 2

We don't need to use any special pointer syntax to dereference the pointer to the pair|car and cdr expect a pointer, and return the eld values of the pair it points to.

car and cdronly work on pairs. If you try to take the car or cdr of anything else, you'll get a

runtime type error. Try it:

Scheme>(car #t)

ERROR: attempt to take the car of a non-pair #t break>,top

Scheme>

The messages you'll see vary from system to system, but the basic idea is the same. We tried to take the car of the boolean #f, which makes no sense because it has no car eld|it doesn't

have any elds. Scheme told is it didn't work, and gave us a break prompt for sorting it out. Then we just used the,topcommand (or whatever works on your system) to tell Scheme to give up on

evaluating that expression and go back to normal interaction.

Scheme also supplies procedures to change the values of a pair's elds, called set-car! and set-cdr!. They take two arguments, a pair and a value for the eld being set.

Scheme>(set-car! my-pair 4) #void Scheme>my-pair (4 . 2) Scheme>(set-cdr! my-pair 5) #void Scheme>my-pair (4 . 5)

The value of the variable my-pair hasn't actually changed, even though it prints dierently. my-pairstill holds a pointer to the same object, the pair we created withcons. What has changed

is the contents of that object. Its elds are like variable bindings, in that they can hold (pointers to) any kind of object, and we've assigned new values to them. (They're value cells.)

We can refer to the same object by another name if we just dene another variable and initialize it with my-pair's value.

Scheme> (define same-pair my-pair) #void

Scheme>same-pair (4 . 5)

Now suppose we assign a new value to the car of the pair, referring to it via my-pair Scheme>(set-car! my-pair 6) #void Scheme>my-pair (6 . 5) Scheme>same-pair (6 . 5)

Notice that the change is visible throughsame-pairas well asmy-pair, because we've changed

Now let's make another pair with the same eld values.

Scheme>(define different-pair (cons 6 5)) different-pair Scheme>different-pair (6 . 5) Scheme>my-pair (6 . 5) Scheme>same-pair (6 . 5)

Notice that we have two dierent pairs, but Scheme prints them out the same way, because it just shows us the structure of data structures. We can't tell that they're dierent just by looking at the printed output. From the printed representation, we can't tell whether or not my-pair, same-pair, anddifferent-pairhold the same values.

Scheme provides a predicate procedure, eq?, to tell whether two objects are the exact same

object.

Scheme>(eq? my-pair same-pair) #t

Scheme>(eq? my-pair different-pair) #f

Scheme>(eq? same-pair different-pair) #f

eq? tests object identity, like pointer comparisons in C (using ==) or Pascal (using =).

It may be confusing, but in programming language terminology, two objects are called identical only if they are the very same object, not just two objects that look alike, like \identical" twins. When the government issues \identity" cards, this is the kind of \identity" we're talking about. Two so-called identical twins have dierent identities, because they're actually dierent people. A pointer is like a a social security number, because it uniquely identies a particular individual object.

Scheme also has a test to see whether objects \look the same," that is, have the same structure. It's called equal?. We call this a structural equivalence test.

Scheme>(equal? my-pair same-pair) #t

Scheme>(equal? my-pair different-pair) #t

Scheme>(equal? same-pair different-pair) #t

different-pair is equal? to my-pair and same-pair because it refers to the same kind of

object, and its eld values areequal?. Notice that that's a recursive denition, which we'll discuss

more when we get to lists.

If we didn't haveeq?, we could still gure out whether two objects were exactly the same object,

by changing one and seeing if the other changed, too.

Scheme>(set-car! my-pair 4) #void Scheme>my-pair (4 . 5) Scheme>same-pair (4 . 5) Scheme>different-pair (6 . 5)

Now I should warn you about set-car! and set-cdr!. The reason we put an exclamation

point in the name of a procedure that side-eects data is because it's dangerous. If you have two pointers to the same data from dierent places, i.e., dierent variable bindings or data structures, it's hard to reason about how changes from one of those places aect things at the other place.

Usually, we like to be able to share data structures, for example, having two data structures that both hold pointers to some third data structure, so the we don't need two copies of it. We don't want to have subtle interactions between dierent procedures that operate on shared data structures.

You should only use side eects when you have a very good reason to, and make it clear that that's what you're doing. Later examples will show how to program in a style that uses very few side eects, and only where they make sense.

Notice that cons is not considered a side-eecting operation, because it returns a new object

that has never been seen before. Somewhere in the implementation of the language, cons side-

eects memory to initialize it, but you don't see that|from your program's point of view, you're getting a new piece of memory that magically has values in place.

================================================================== This is the end of Hunk D.

At this point, you should go back to the previous chapter and read Hunk E before returning here and continuing this tutorial. ==================================================================

(Go BACK to read Hunk E, which starts at Section 2.2.10 The Empty List], page 37.)