Common Lisp the Language, 2nd Edition

Next: How to Use Up: Structures Previous: Structures

# 19.1. Introduction to Structures

The structure facility is embodied in the `defstruct` macro, which allows the user to create and use aggregate data types with named elements. These are like ``structures'' in PL/I, or ``records'' in Pascal.

As an example, assume you are writing a Lisp program that deals with space ships in a two-dimensional plane. In your program, you need to represent a space ship by a Lisp object of some kind. The interesting things about a space ship, as far as your program is concerned, are its position (represented as x and y coordinates), velocity (represented as components along the x and y axes), and mass.

A ship might therefore be represented as a record structure with five components: x-position, y-position, x-velocity, y-velocity, and mass. This structure could in turn be implemented as a Lisp object in a number of ways. It could be a list of five elements; the x-position could be the car, the y-position the cadr, and so on. Equally well it could be a vector of five elements: the x-position could be element 0, the y-position element 1, and so on. The problem with either of these representations is that the components occupy places in the object that are quite arbitrary and hard to remember. Someone looking at `(cadddr ship1)` or `(aref ship1 3)` in a piece of code might find it difficult to determine that this is accessing the y-velocity component of `ship1`. Moreover, if the representation of a ship should have to be changed, it would be very difficult to find all the places in the code to be changed to match (not all occurrences of `cadddr` are intended to extract the y-velocity from a ship).

Ideally components of record structures should have names. One would like to write something like `(ship-y-velocity ship1)` instead of `(cadddr ship1)`. One would also like a more mnemonic way to create a ship than this:

    (list 0 0 0 0 0)

Indeed, one would like `ship` to be a new data type, just like other Lisp data types, that one could test with `typep`, for example. The `defstruct` facility provides all of this.

`defstruct` itself is a macro that defines a structure. For the space ship example, one might define the structure by saying:

    (defstruct ship
      x-position
      y-position
      x-velocity
      y-velocity
      mass)

This declares that every `ship` is an object with five named components. The evaluation of this form does several things:

- It defines `ship-x-position` to be a function of one argument, a ship, that returns the x-position of the ship; `ship-y-position` and the other components are given similar function definitions. These functions are called the access functions, as they are used to access elements of the structure.

- The symbol `ship` becomes the name of a data type of which instances of ships are elements. This name becomes acceptable to `typep`, for example; `(typep x 'ship)` is true if `x` is a ship and false if `x` is any object other than a ship.

- A function named `ship-p` of one argument is defined; it is a predicate that is true if its argument is a ship and is false otherwise.

- A function called `make-ship` is defined that, when invoked, will create a data structure with five components, suitable for use with the access functions. Thus executing

      (setq ship2 (make-ship))

  sets `ship2` to a newly created `ship` object. One can specify the initial values of any desired component in the call to `make-ship` by using keyword arguments in this way:

      (setq ship2 (make-ship :mass *default-ship-mass*
                             :x-position 0
                             :y-position 0))

  This constructs a new ship and initializes three of its components. This function is called the constructor function because it constructs a new structure.

- The `#S` syntax can be used to read instances of `ship` structures, and a printer function is provided for printing out ship structures. For example, the value of the variable `ship2` shown above might be printed as

      #S(ship  x-position 0  y-position 0  x-velocity nil
               y-velocity nil  mass 170000.0)

- A function called `copy-ship` of one argument is defined that, when given a `ship` object, will create a new `ship` object that is a copy of the given one. This function is called the copier function.

- One may use `setf` to alter the components of a `ship`:

      (setf (ship-x-position ship2) 100)

  This alters the x-position of ship2 to be `100`. This works because `defstruct` behaves as if it generates an appropriate `defsetf` form for each access function.

This simple example illustrates the power of `defstruct` to provide abstract record structures in a convenient manner. `defstruct` has many other features as well for specialized purposes.

Next: How to Use Up: Structures Previous: Structures