10.1 Список свойств

Начиная с самого создания, Lisp для каждого символа ассоциирует табличную структуру данных, называемую список свойств (для краткости plist). Список свойств содержит ноль и более элементов. Каждый элемент содержит ключ (называемым индикатором), который чаще всего является символом, и ассоциированным с ним значением (называемым иногда свойством), которое может быть любым Lisp’овым объектом. Среди индикаторов не может быть дубликатов. Список свойств может иметь только одно свойство для данного имени. Таким образом, значение может получено с помощью двух символов: исходного символа и индикатора.

Список свойств по целевому назначению очень похож на ассоциативный список. Различие в том, что список свойств является единственно подлинным объектом. Операции добавления и удаления элементов деструктивны, то есть при их использовании изменяется старый список, и нового списка свойств не создаётся. Ассоциативные список, наоборот, обычно изменяются недеструктивно (без побочных эффектов) с помощью добавления в начало новых элементов (смотрите acons и pairlis).

Список свойств реализуется, как ячейка памяти, содержащая список с чётным (возможно нулевым) количеством аргументов. (Обычно эта ячейка памяти является ячейкой списка свойств в символе, но в принципе подходит любая ячейка памяти, к которой можно применить setf) Каждая пара элементов в списке составляет строку. Первый в паре это индикатор, а второй — значение. Так как функции для списка свойств используют символ, а не сам список, то изменения этого списка свойств может быть записаны сохранением обратно в ячейку списка свойств символа. FIXME

Когда создаётся символ, его список свойств пуст. Свойства создаются с помощью get внутри формы setf.

Common Lisp не использует список свойств символа так интенсивно, как это делали ранние реализации Lisp’а. В Common Lisp’е нечасто используемые данные, такие как отладочная информация, информация для компилятора и документация, хранятся в списках свойств.

[Функция] get symbol indicator &optional default

get ищет в списке свойств символа symbol индикатор равный eq индикатору indicator. Первый аргумент должен быть символом. Если такое свойство найдено, возвращается её значение. Иначе возвращается значение default.

Если default не указано, тогда для значения по-умолчанию используется nil.

Следует отметить, что способа отличить значения по-умолчанию и такое же значение свойства нет:

(get x y)  (getf (symbol-plist x) y)

Допустим, что список свойств символа foo является (bar t baz 3 hunoz "Huh?"). Тогда, например:

(get ’foo ’baz)  3
(get ’foo ’hunoz)  "Huh?"
(get ’foo ’zoo)  nil

setf может использоваться вместе с get для создания новой пары свойства, возможно замещая старую пару с тем же именем. Например:

(get ’clyde ’species)  nil
(setf (get ’clyde ’species) ’elephant)  elephant
и теперь (get ’clyde ’species)  elephant

В данном контексте может быть указан аргумент default. Он игнорируется в setf, но может быть полезен в таких макросах, как push, которые связаны с setf:

(push item (get sym ’token-stack ’(initial-item)))

означает то же, что и

(setf (get sym ’token-stack ’(initial-item))
      (cons item (get sym ’token-stack ’(initial-item))))

а если упростить, то

(setf (get sym ’token-stack)
      (cons item (get sym ’token-stack ’(initial-item))))

X3J13 voted in March 1989 to clarify the permissible side effects of certain operations; (setf (get symbol indicator) newvalue) is required to behave exactly the same as (setf (getf (symbol-plist symbol) indicator) newvalue).


[Функция] remprop symbol indicator

Эта функция удаляет из символа symbol свойство с индикатором, равным eq индикатору indicator. Индикатор свойства и соответствующее значение удаляется из списка деструктивной склейкой списка свойств. Она возвращает nil, если указанного свойства не было, или не-nil, если свойство было.

(remprop x y)  (remf (symbol-plist x) y)

Например, если список свойств foo равен

(color blue height 6.3 near-to bar)

тогда вызов

(remprop ’foo ’height)

вернёт значение не-nil, после изменения списка свойств символа foo на

(color blue near-to bar)

X3J13 voted in March 1989 to clarify the permissible side effects of certain operations; (remprop symbol indicator) is required to behave exactly the same as (remf (symbol-plist symbol) indicator).


[Функция] symbol-plist symbol

Эта функция возвращает список, который содержит пары свойств для символа symbol. Извлекается содержимое ячейки списка свойств и возвращается в качестве результата.

Следует отметить, что использование get с результатом symbol-plist не будет работать. Необходимо передавать в get символ, или же использовать getf.

С symbol-plist может использоваться setf для деструктивной замены списка свойств символа. Это относительно опасная операция, так как может уничтожить важную информацию, которую, возможно, хранила там реализация. Также, позаботьтесь о том, чтобы новый список свойств содержал чётное количество элементов.


[Функция] getf place indicator &optional default

getf ищет индикатор равный eq indicator в списке свойств, находящимся в place. Если он найден, тогда возвращается соответствующее значение. Иначе возвращается default. Если default не задан, то значение по-умолчанию используется nil. Следует отметить, что метода определения, вернулось ли значение по умолчанию или это значение свойства, нет. Часто place вычисляется из обобщённой переменной, принимаемой функцией setf.

setf может использоваться вместе getf, и в этом случае place должно быть обобщённым, чтобы его можно было передать в setf. Целью является добавление новой пары свойства, или изменению уже существующей пары, в списке свойств, хранящимся в place. В данном контексте может быть использован аргумент default. Он игнорируется функцией setf, но может быть полезен в таких макросах, как push, которые связаны с setf. Смотрите описание get для примера.

X3J13 voted in March 1989 to clarify the permissible side effects of certain operations; setf used with getf is permitted to perform a setf on the place or on any part, car or cdr, of the top-level list structure held by that place.

X3J13 voted in March 1988 to clarify order of evaluation (see section 7.2).


[Макрос] remf place indicator

Эта функция в списке свойств, хранящимся в place, удаляет свойства, индикатор которого равен eq аргументу indicator. Индикатор свойства и соответствующее значение удаляется из списка деструктивной склейкой. remf возвращает nil, если свойства не было в списке, и некоторое не-nil значение, если свойство было. Форма place может быть любой обобщённой переменной, принимаемой setf. Смотрите remprop.

X3J13 voted in March 1989 to clarify the permissible side effects of certain operations; remf is permitted to perform a setf on the place or on any part, car or cdr, of the top-level list structure held by that place.

X3J13 voted in March 1988 to clarify order of evaluation (see section 7.2).


[Функция] get-properties place indicator-list

get-properties похожа на getf за исключением того, что второй аргумент является списком индикаторов. get-properties ищет первый встретившийся индикатор, который есть в списке индикаторов. Обычно place вычисляется из обобщённой переменной, которая может использоваться в setf.

get-properties возвращает три значения. Если было найдено свойство, то первые два значения являются индикатором и значением для первого свойства, чей индикатор присутствовал в списке indicator-list, и третье значение является остатком списка свойств, car элемент которого был индикатором (и соответственно cadr элемент — значением). Если ни одного свойства не было найдено, то все три значения равны nil. Третье значение является флагом успешности или неудачи, и позволяет продолжить поиск свойств в оставшемся списке.