В Lisp с символом можно связать именованные свойства. Свойства символа записываются в вектор свойств. У каждого символа разные свойства.
Вектор свойств может быть пуст или содержать произвольное количество свойств. Свойства отсортированы по имени. Его форма такова:
#((имя1 . значение1) (имя2 . значение2) … (имяn . значениеn))
Например, у символа ягода-рябины может быть такой вектор свойств:
#((вкус . кислый) (цвет . красный))
Вектор свойств символа можно обновлять или удалять с помощью дополнительных методов. Программист должен сам предусматривать и обрабатывать интересующие его свойства.
С символом связаны лишь его имя, произвольное, назначенное функцией присваивания (setq), значение и назначенное определением класса и методов (defclass, defmethod, …). Значение и определение класса являются встроенными системными свойствами, которые управляют работой интерпретатора в различных ситуациях. Весь список свойств также является системным свойством. Работающие со свойствами символов прикладные системы могут свободно определять новые свойства.
Далее мы рассмотрим псевдофункции для чтения, изменения и удаления свойств, определяемых пользователем.
Выяснить значение свойства, связанного с символом, можно с помощью функции get:
(символ get свойство)
Если, например, с символом ягода-рябины связан определённый нами ранее вектор свойств, то мы получим следующие результаты:
>('ягода-рябины get 'вкус) кислый >('ягода-рябины get 'вес) nil
Так как у символа ягода-рябины нет свойства вес, то get вернёт значение nil.
Присваивание нового свойства или изменение значения существующего свойства осуществляется функцией put:
(символ put свойство значение)
Свойства символов находятся в связанных с символами ячейках памяти. Приведём пример:
>('ягода-рябины put 'вес '(2 г)) (2 г) >('ягода-рябины get 'вес) (2 г)
Побочным эффектом вызова будет изменение вектора свойств символа ягода-рябины следующим образом:
#((вес 2 г) (вкус . кислый) (цвет . красный))
Удаление свойства и его значения осуществляется функцией remprop:
(символ remprop свойство)
Приведём пример:
>('ягода-рябины remprop 'вкус) true >('ягода-рябины get 'вкус) nil
Функция remprop возвращает в качестве значения true или false. Если удаляемого свойства нет, то возвращается false. Свойство можно удалить, присвоив ему значение nil.
Читать из вектора свойств, создавать и обновлять в нём свойства можно не только по отдельности, но и целиком. Например, значением вызова
(символ properties)
является весь вектор свойств:
>('ягода-рябины properties) #((вес 2 г) (цвет . красный))
Свойства символов независимо от их значений доступны из всех контекстов до тех пор, пока они не будут явно изменены или удалены. Использование символа в качестве функции или переменной, т.е. изменение значения символа или определения функции, не влияет на другие свойства символа, и они сохраняются.
Наличие свойств полезно как для поддержки самой системы, так и во многих типичных случаях представления данных. Использование свойств даёт средства для рассматриваемого позже программирования, управляемого данными, с помощью которого можно реализовать различные языки представления знаний и формализмы, такие как семантические сети, фреймы и объекты объектно-ориентированного программирования.