common-lisp Types of Lists Property Lists


Plain lists are useful for representing a sequence of elements, but sometimes it is more helpful to represent a kind of key to value mapping. Common Lisp provides several ways to do this, including genuine hash tables (see 18.1 Hash Table Concepts). There are two primary ways or representing key to value mappings in Common Lisp: property lists and association lists. This example describes property lists.

A property list, or plist, is a "plain" list in which alternating values are interpreted as keys and their associated values. For instance:

(defparameter *ages* (list 'john 34 'mary 23 'tim 72))

can be considered as a property list that maps symbols indicating a personal name with an integer indicating age. It is possible to implement some retrieval functions using plain list functions, like member. For instance, to retrieve the age of john, one could write

(second (member 'mary *age*))
;=> 23

The member function returns the tail of the list beginning with mary, that is, (mary 23 tim 72), and second returns the second element of that list, that is 23. While this is one way to access values in a property list, the purpose of a convention like property lists is to abstract away from the underlying representation (a list) and to provide higher-level functions for working with the data structure.

For property lists, the retrieval function is getf, which takes the property list, a key (more commonly called an indicator), and an optional default value to return in case the property list does not contain a value for the key.

(getf *ages* 'tim)
;=> 72

(getf *ages* 'bob -1)
;=> -1

For updating values in a property list, setf may be used. For instance, when john's birthday arrives and his age increases, either of the following could be performed:

(setf (getf *ages* 'john) 35)

(incf (getf *ages* 'john))

incf works in this case because it is based on setf.

To look up multiple properties in a property list as once, use get-properties.

The getf function searches through the list from left to right, which means that is is possible to "mask" values in a property list without removing them from a list or updating any of the structure of the list. For instance, using list*:

(defvar *ages* '(john 34 mary 23 tim 72))

(defvar *new-ages* (list* 'mary 29 *ages*))

;=> (mary 29 john 34 mary 23 tim 72)

And now, a lookup for mary will return the first entry:

(getf *new-ages* 'mary)
;=> 29