Встроенные функции
Встроенная функция — это функция, которая работает так же, как обычная функция, за одним исключением: при компиляции вызова этой функции в байткод (см. Компиляция в байткод) определение функции подставляется в вызывающий код.
Простейший способ определить встроенную функцию — написать defsubst вместо defun. Остальная часть определения выглядит точно так же, но использование defsubst указывает, что функцию следует встроить при компиляции в байткод.
- Макрос: defsubst name args [doc] [declare] [interactive] body…
Этот макрос определяет встроенную функцию. Его синтаксис в точности совпадает с синтаксисом
defun(см. Определение функций).
Встраивание функции часто ускоряет выполнение её вызовов. Однако у этого есть и недостатки. Во-первых, это снижает гибкость: если изменить определение функции, уже встроенные вызовы продолжат использовать старое определение, пока их не скомпилируют заново.
Другой недостаток состоит в том, что встраивание большой функции может увеличить размер скомпилированного кода как в файлах, так и в памяти. Поскольку преимущество встроенных функций в скорости наиболее заметно для небольших функций, обычно не следует встраивать большие функции.
Кроме того, встроенные функции плохо подходят для отладки, трассировки и добавления советов (см. Добавление советов к функциям Emacs Lisp). Поскольку простота отладки и гибкость переопределения функций — важные особенности Emacs, не следует встраивать функцию, даже если она небольшая, если только её скорость не имеет решающего значения и вы не проверили время выполнения кода, чтобы убедиться, что использование defun действительно приводит к проблемам с производительностью.
После определения встроенной функции её встраивание можно выполнить позднее в том же файле, как и для макросов.
Можно использовать defmacro, чтобы определить макрос, раскрывающийся в тот же код, который выполняла бы встроенная функция (см. Макросы). Однако такой макрос можно будет использовать только непосредственно в выражениях — вызвать макрос с помощью apply, mapcar и так далее нельзя. Кроме того, преобразование обычной функции в макрос требует некоторых усилий. Преобразовать её во встроенную функцию просто: достаточно заменить defun на defsubst. Поскольку каждый аргумент встроенной функции вычисляется ровно один раз, вам не нужно беспокоиться о том, сколько раз тело функции использует аргументы, как это приходится делать с макросами.
В качестве альтернативы можно определить функцию, предоставив код, который будет встраивать её как макрос компилятора (см. Форма declare). Это можно сделать с помощью следующих макросов.
- Макрос: define-inline name args [doc] [declare] body…
-
Определяет функцию name, предоставляя код для её встраивания в виде макроса компилятора. Функция принимает список аргументов args и имеет указанное тело body.
Если указан doc, он должен быть строкой документации функции (см. Строки документации функций); если указан declare, он должен быть формой
declare(см. Формаdeclare), задающей метаданные функции. Помимо обычных объявлений, declare может включать(noinline NOINLINE), когда не-nilNOINLINE запрещает Emacs встраивать определённую функцию.
Функции, определённые с помощью define-inline, имеют ряд преимуществ перед макросами, определёнными с помощью defsubst или defmacro:
- Их можно передавать в
mapcar(см. Функции отображения). - Они эффективнее.
- Их можно использовать в качестве форм-мест для сохранения значений (см. Обобщённые переменные).
- Они ведут себя более предсказуемо, чем
cl-defsubst(см. Списки аргументов в Расширения Common Lisp для GNU Emacs Lisp).
Как и defmacro, функция, встроенная с помощью define-inline, наследует правила области видимости — динамической или лексической — из места вызова. См. Правила области видимости для связываний переменных.
Следующие макросы следует использовать в теле функции, определённой с помощью define-inline.
- Макрос: inline-quote expression
Заключает expression в кавычки для
define-inline. Это похоже на обратную кавычку (см. Обратная кавычка), но заключает в кавычки код и принимает только,, но не,@.
- Макрос: inline-letevals (bindings…) body…
-
Этот макрос позволяет удобно гарантировать, что аргументы встроенной функции вычисляются ровно один раз, а также создавать локальные переменные.
Он похож на
let(см. Локальные переменные): он создаёт локальные переменные, заданные в bindings, а затем вычисляет body в области действия этих связываний.Каждый элемент bindings должен быть либо символом, либо списком вида
(var expr); в результате вычисляется expr, а результат связывается с var. Однако если элемент bindings — это просто символ var, результат вычисления var повторно связывается с var (что существенно отличается от работыlet).Хвост bindings может быть либо
nil, либо символом, который должен содержать список аргументов; в последнем случае вычисляется каждый аргумент, а символ связывается с полученным списком.
- Макрос: inline-const-p expression
Возвращает значение, отличное от
nil, если значение expression уже известно.
- Макрос: inline-const-val expression
Возвращает значение expression.
- Макрос: inline-error format &rest args
Выдаёт сообщение об ошибке, форматируя args согласно format.
Вот пример использования define-inline:
(define-inline myaccessor (obj)
(inline-letevals (obj)
(inline-quote (if (foo-p ,obj) (aref (cdr ,obj) 3) (aref ,obj 2)))))
Это эквивалентно следующему:
(defsubst myaccessor (obj) (if (foo-p obj) (aref (cdr obj) 3) (aref obj 2)))
Copyright © 1990-1996, 1998-2026 Free Software Foundation, Inc.
Licensed under the GNU GPL license.
https://www.gnu.org/software/emacs/manual/html_node/elisp/Inline-Functions.html