Общие функции
Функции, определённые с помощью defun, имеют жёстко заданный набор предположений о типах и ожидаемых значениях своих аргументов. Например, функция, разработанная для обработки значений аргумента, которые являются либо числами, либо списками чисел, завершится ошибкой или выдаст сообщение об ошибке, если вызвана со значением любого другого типа, например, вектора или строки. Это происходит потому, что реализация функции не подготовлена к обработке типов, отличных от тех, которые предполагались при проектировании.
В противоположность этому, объектно-ориентированные программы используют полиморфные функции: набор специализированных функций с одинаковым именем, каждая из которых написана для определённого набора типов аргументов. Которая из функций будет фактически вызвана, определяется во время выполнения на основе типов фактических аргументов.
Emacs предоставляет поддержку полиморфизма. Как и другие среды Lisp, в частности Common Lisp и его система объектно-ориентированного программирования (CLOS), эта поддержка основана на общих функциях. Общие функции Emacs тесно следуют за CLOS, включая использование аналогичных имён, поэтому, если у вас есть опыт работы с CLOS, остальная часть этого раздела покажется вам очень знакомой.
Общая функция определяет абстрактную операцию, указав её имя и список аргументов, но (обычно) без реализации. Фактическая реализация для нескольких конкретных классов аргументов предоставляется методами, которые должны быть определены отдельно. Каждый метод, реализующий общую функцию, имеет то же имя, что и общая функция, но определение метода указывает, какие типы аргументов он может обрабатывать, специализируя аргументы, определённые общей функцией. Эти специализаторы аргументов могут быть более или менее специфичными; например, тип string более специфичен, чем более общий тип, такой как sequence.
Обратите внимание, что в отличие от языков ООП на основе сообщений, таких как C++ и Simula, методы, реализующие общие функции, не принадлежат классу, они принадлежат общей функции, которую они реализуют.
Когда вызывается общая функция, она выбирает применимые методы, сравнивая фактические аргументы, переданные вызывающим объектом, со специализаторами аргументов каждого метода. Метод применим, если фактические аргументы вызова совместимы со специализаторами метода. Если применимо более одного метода, они объединяются по определённым правилам, описанным ниже, а затем комбинация обрабатывает вызов.
- Макрос: cl-defgeneric имя аргументы [документация] [опции-и-методы…] &rest тело
-
Этот макрос определяет общую функцию с указанным именем и аргументами. Если тело присутствует, оно предоставляет реализацию по умолчанию. Если документация присутствует (она должна всегда присутствовать), она определяет строку документации для общей функции в формате
(:documentation docstring). Дополнительные опции-и-методы могут иметь один из следующих форматов:(declare declarations)Форма объявления, как описано в Форме объявления.
(:argument-precedence-order &rest args)Эта форма влияет на порядок сортировки при объединении применимых методов. Обычно, когда два метода сравниваются во время объединения, аргументы метода проверяются слева направо, и первый метод, специализатор аргумента которого более специфичен, будет стоять раньше другого. Порядок, определённый этой формой, переопределяет это, и аргументы проверяются в соответствии с их порядком в этой форме, а не слева направо.
(:method [qualifiers…] args &rest body)Эта форма определяет метод так же, как и
cl-defmethod.
- Макрос: cl-defmethod имя [доп.] [квалификатор] аргументы [&контекст (выраж. спец.)…] &rest [строка_документации] тело
-
Этот макрос определяет конкретную реализацию для общей функции с именем имя. Код реализации задаётся телом. Если присутствует строка_документации, это строка документации для метода. Список аргументов, который должен быть идентичным во всех методах, реализующих общую функцию, и должен совпадать со списком аргументов этой функции, предоставляет специализаторы аргументов в формате
(arg spec), где арг — имя аргумента, как указано в вызовеcl-defgeneric, а спец — одна из следующих форм специализатора:typeЭтот специализатор требует, чтобы аргумент был заданного типа тип, одного из типов из иерархии типов, описанной ниже.
(eql object)Этот специализатор требует, чтобы аргумент был
eqlзаданному объекту.(head object)Аргумент должен быть ячейкой cons,
carкоторойeqlобъекту.struct-typeАргумент должен быть экземпляром класса с именем тип_структуры, определённого с помощью
cl-defstruct(см. Структуры в Расширения Common Lisp для GNU Emacs Lisp), или одного из его дочерних классов.
Определения методов могут использовать новый ключевой аргумент,
&context, который вводит дополнительные специализаторы, которые проверяют среду в момент выполнения метода. Этот ключевой аргумент должен располагаться после списка обязательных аргументов, но перед любыми ключевыми словами&restили&optional. Специализаторы&contextочень похожи на обычные специализаторы аргументов — (выраж спец) — за исключением того, что выраж — это выражение, которое должно быть вычислено в текущем контексте, а спец — это значение для сравнения. Например,&context (overwrite-mode (eql t))сделает метод применимым только тогда, когдаoverwrite-modeвключено. Ключевое слово&contextможет быть после которого идёт любое количество специализаторов контекста. Поскольку специализаторы контекста не являются частью подписи аргумента общей функции, их можно опустить в методах, которые их не требуют.Специализатор типа,
(arg type), может указать один из системных типов в следующем списке. Когда указан родительский тип, аргумент, тип которого является любым из его более специфичных дочерних типов, а также внуков, правнуков и т.д., также будет совместим.integerРодительский тип:
number.numbernullРодительский тип:
symbolsymbolstringРодительский тип:
array.arrayРодительский тип:
sequence.consРодительский тип:
list.listРодительский тип:
sequence.markeroverlayfloatРодительский тип:
number.window-configurationprocesswindowsubrcompiled-functionbufferchar-tableРодительский тип:
array.bool-vectorРодительский тип:
array.vectorРодительский тип:
array.framehash-tablefont-specfont-entityfont-object
Дополнительный элемент доп, выраженный как ‘:extra строка’, позволяет добавить больше методов, отличающихся строкой, для тех же специализаторов и квалификаторов.
Дополнительный квалификатор позволяет комбинировать несколько применимых методов. Если он отсутствует, определённый метод является главным методом, отвечающим за предоставление основной реализации общей функции для специализированных аргументов. Вы также можете определить вспомогательные методы, используя одно из следующих значений в качестве квалификатора:
:beforeЭтот вспомогательный метод будет выполняться до основного метода. Точнее, все
:beforeметоды будут выполняться до основного в порядке от наиболее к наименее специфичным.:afterЭтот вспомогательный метод будет выполняться после основного метода. Точнее, все такие методы будут выполняться после основного, в порядке от наименее к наиболее специфичным.
:aroundЭтот вспомогательный метод будет выполняться вместо основного метода. Наиболее специфичный из таких методов будет выполняться до любого другого метода. Такие методы обычно используют
cl-call-next-method, описанную ниже, для вызова других вспомогательных или основных методов.
Функции, определённые с помощью
cl-defmethod, не могут быть сделаны интерактивными, т.е. командами (см. Определение команд), добавив к ним формуinteractive. Если вам нужна полиморфная команда, мы рекомендуем определить обычную команду, которая вызывает полиморфную функцию, определённую с помощьюcl-defgenericиcl-defmethod.
Каждый раз, когда вызывается общая функция, она создаёт эффективный метод, который будет обрабатывать этот вызов путём объединения применимых методов, определённых для функции. Процесс поиска применимых методов и создания эффективного метода называется диспетчеризацией. Применимые методы — это те, специализаторы которых совместимы с фактическими аргументами вызова. Поскольку все аргументы должны быть совместимы со специализаторами, все они определяют, применим ли метод. Методы, которые явно специализируют более одного аргумента, называются методами множественной диспетчеризации.
Применимые методы сортируются в порядке их объединения. Метод, специализатор левого аргумента которого является наиболее специфичным, будет стоять первым в порядке. (Указание :argument-precedence-order как части cl-defmethod переопределяет это, как описано выше.) Если тело метода вызывает cl-call-next-method, следующий наиболее специфичный метод будет выполнен. Если существуют применимые :around методы, наиболее специфичный из них будет выполнен первым; он должен вызвать cl-call-next-method для выполнения любых менее специфичных :around методов. Затем выполнение идёт за :before методами в порядке их специфичности, за которыми следует основной метод, а в конце — :after методы в обратном порядке их специфичности.
- Функция: cl-call-next-method &rest args
При вызове из лексического тела основного или
:aroundвспомогательного метода, вызывает следующий применимый метод для той же обобщенной функции. Обычно вызывается без аргументов, что означает вызов следующего применимого метода с теми же аргументами, что и вызывающий метод. В противном случае используются указанные аргументы.
- Функция: cl-next-method-p
Эта функция, вызываемая из лексического тела основного или
:aroundвспомогательного метода, возвращает не-nil, если существует следующий вызываемый метод.
Copyright © 1990-1996, 1998-2022 Free Software Foundation, Inc.
Licensed under the GNU GPL license.
https://www.gnu.org/software/emacs/manual/html_node/elisp/Generic-Functions.html