Встроенная функция — это функция, которая работает как обычная функция, за исключением одного: когда вы байт-компилируете вызов функции (см. Байт-компиляция), определение функции расширяется в вызывающий код.
Простой способ определения встроенной функции — использовать
defsubst вместо defun. Остальная часть определения
выглядит так же, но использование defsubst указывает, что она должна быть встроенной
для байт-компиляции.
Этот макрос определяет встроенную функцию. Его синтаксис в точности такой же, как у defun (см. Определение функций).
Превращение функции во встроенную часто ускоряет ее вызовы. Но у нее также есть недостатки. Во-первых, это снижает гибкость; если вы измените определение функции, вызовы, которые уже были встроены, продолжат использовать старое определение до тех пор, пока вы их не перекомпилируете.
Другой недостаток заключается в том, что превращение большой функции во встроенную может увеличить размер скомпилированного кода как в файлах, так и в памяти. Поскольку преимущество в скорости встроенных функций наибольшее для небольших функций, как правило, не следует делать большие функции встроенными.
Кроме того, встроенные функции плохо работают с точки зрения отладки,
трассировки и консультирования (см. Консультирование функций Emacs Lisp). Поскольку простота
отладки и гибкость переопределения функций являются важными
функциями Emacs, вы не должны делать функцию встроенной, даже если она мала, если только ее скорость не является действительно критичной, и вы не измерили код,
чтобы убедиться, что использование defun действительно вызывает проблемы с производительностью.
После определения встроенной функции ее встроенное расширение может быть выполнено позже в том же файле, подобно макросам.
Возможно использовать defmacro для определения макроса, который
расширяется в тот же код, который выполнила бы встроенная функция
(см. Макросы). Но макрос будет ограничен прямым использованием в
выражениях — макрос не может быть вызван с помощью apply,
mapcar и так далее. Кроме того, для преобразования обычной функции в макрос требуется некоторая работа. Преобразование ее во встроенную функцию
легко; просто замените defun на defsubst. Поскольку каждый аргумент встроенной функции вычисляется ровно один раз, вам не нужно
беспокоиться о том, сколько раз тело использует аргументы, как это происходит с макросами.
В качестве альтернативы вы можете определить функцию, предоставив код, который
будет ее встраивать как компиляторный макрос (см. Форма declare). Следующие макросы делают это возможным.
Определяет функцию name, предоставляя код, который выполняет ее встраивание, как компиляторный макрос. Функция примет список аргументов args и будет иметь указанное body.
Если присутствует, doc должен быть строкой документации функции
(см. Строки документации функций); declare, если присутствует, должен быть
формой declare (см. Форма declare), указывающей метаданные функции.
Функции, определенные с помощью define-inline, имеют несколько преимуществ
по сравнению с макросами, определенными с помощью defsubst или defmacro:
mapcar (см. Функции отображения).
cl-defsubst
(см. Списки аргументов в Common Lisp Extensions for GNU Emacs
Lisp).
Как и defmacro, функция, встроенная с помощью define-inline,
наследует правила области видимости, динамические или лексические, от места вызова. См. Правила области видимости для привязок переменных.
Следующие макросы следует использовать в теле функции, определенной
с помощью define-inline.
Квотирует expression для define-inline. Это похоже на
обратную кавычку (см. Обратная кавычка), но квотирует код и принимает только
,, а не ,@.
Это предоставляет удобный способ гарантировать, что аргументы встроенной функции вычисляются ровно один раз, а также создавать локальные переменные.
Это похоже на let (см. Локальные переменные): он устанавливает локальные
переменные, указанные в bindings, а затем вычисляет
body с этими привязками.
Каждый элемент bindings должен быть либо символом, либо списком
в форме (var expr); результатом является вычисление
expr и привязка var к результату. Однако, когда элемент
bindings является просто символом var, результат вычисления
var перепривязывается к var (что сильно отличается от
способа работы let).
Хвост bindings может быть либо nil, либо символом, который
должен содержать список аргументов, в этом случае каждый аргумент вычисляется, и символ
привязывается к полученному списку.
Возвращает не-nil, если значение expression уже
известно.
Возвращает значение expression.
Сигнализирует об ошибке, форматируя 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)))