Я много читал об универсальных функциях в CL. Я понимаю. И я понимаю, почему они ценны.
В основном я использую их, когда хочу выполнить аналогичное действие с разными типами данных, например:
(defgeneric build-url (account-key)
(:documentation "Create hunter api urls"))
(defmethod build-url ((key number))
"Build lead api url"
(do-something...))
(defmethod build-url ((key string))
"build campaign api url"
(do-somthing... ))
В этом примере campaign-url
и lead-url
являются структурами (defstruct).
Мой вопрос: на высоком уровне, как классы повышают ценность того, как общие функции + структуры работают вместе?
Структуры исторически предшествовали классам, они более ограничены и более «статичны», чем классы: как только структура определена, компилятор может генерировать код, который эффективно обращается к ее слотам, может предполагать, что их расположение фиксировано и т. д. Существует много встраивания или макросов. -сделано расширение, которое заставляет перестраивать все с нуля при изменении структуры. Возможность переопределить структуру во время выполнения не определяется стандартом, это просто реализация, пытающаяся быть хорошей.
С другой стороны, классы имеют больше возможностей и ими легче манипулировать во время выполнения. Предположим, вы пишете этот класс:
(defclass person ()
((name :initarg :name :reader .name)))
И вы создаете его:
(defparameter *someone* (make-instance 'person :name "Julia O'Caml"))
Теперь можно обновить определение класса:
(defparameter *id-counter* 0)
(defun generate-id ()
(incf *id-counter*))
(defclass person ()
((name :initarg :name :reader .name)
(dob :initarg :date-of-birth :reader .date-of-birth)
(%id :reader .id :initform (generate-id))))
И теперь у *someone*
, который уже существовал, есть два дополнительных поля: dob
, которое не привязано, и %id
, которое автоматически инициализируется до 1. Существует целый раздел о Создание и инициализация объектов (7.1), который определяет, как объекты могут быть переопределить, изменить класс и т. д.
Более того, этот механизм не является фиксированным, многие шаги, описанные выше, основаны на универсальных функциях. Можно определить, как объект выделяется, инициализируется и т. д. Концепция была стандартизирована как то, что известно как Протокол метаобъектов, который также вводит концепцию метаобъекта, объекта, представляющего класс: обычно класс имеет имя, родительские классы, слоты и т. д., но вы можете добавить новых членов в класс или изменить организацию слотов экземпляров (возможно, вам просто нужен глобальный дескриптор и соединение, а фактические слоты экземпляров хранятся в другом процессе ?).
Также обратите внимание, что после того, как CLOS/MOP был определен, в конечном итоге также стало возможным определять структуры в этой структуре: в стандарте defstruct
(без опции :type
) определяет классы с метаклассом structure-class
. Тем не менее, они не ведут себя как standard-class
, потому что, как сказано выше, они более ограничены и, как таковые, подвергаются более агрессивной оптимизации компиляции (в целом).
Структуры хороши, если вам нужно программировать, как на C, и вы можете перекомпилировать весь свой код при изменении структуры. Однако использовать их во всех случаях преждевременно. В настоящее время можно использовать множество стандартных объектов, не замечая большой медлительности (немного похоже на Python).
да, это то, что я имел в виду, то, что делает структуры привлекательными, - это их короткий синтаксис defstruct, но есть макросы, чтобы иметь это с defclass в библиотеках.
It is however premature optimization to use them in all cases
- вы имеете в виду занятия здесь?