Кнуто: Райнер Йосвиг, возможно, просит вас подумать о том, как работает утверждение дела. А именно, что после оценки ключевой формы (т. Е. Первого аргумента) она будет сравниваться последовательно с ключом в каждом предложении, пока не будет найдено совпадение. Сравнение может занять много времени, если есть много предложений. Вы можете узнать это, внимательно прочитав запись для case
в Гиперспеке (как он неоднократно настаивал на этом):
Форма ключа или место ключа оценивается для создания тестового ключа. Каждый из
нормальные предложения затем рассматриваются по очереди.
Также обратите внимание, что построение множества предложений case
добавит время для расширения и компиляции макроса во время компиляции.
Что касается использования eval
в nth-expr%%
, вы все равно можете добиться эффекта eval, переключившись на apply
:
(defmacro nth-expr%% (n &rest es)
`(let ((ne (nth ,n ',es)))
(apply (car ne) (cdr ne))))
Но см. «Затвор утечки» в http://www.gigamonkeys.com/book/macros-defining-your-own.html, чтобы узнать о более надежной обработке.
В целом, более эффективный способ обработки выражений - это простой вектор, а не список. (Постановка задачи не исключает векторное представление.) Хотя nth
и case
предполагают поиск по выражениям один за другим, такая функция, как aref
или svref
, может напрямую индексировать ее. Предполагая, что вектор выражений передается в макрос вместе с индексом, возможно, сначала требуется (coerce expressions 'simple-vector)
, если список, то результат может быть вычислен за постоянное время независимо от количества выражений:
(defmacro nth-expr%%% (n es)
`(let ((ne (svref ',es ,n)))
(apply (car ne) (cdr ne))))
так что теперь
(defvar i 1)
(nth-expr%%% (1+ i) #((+ 2 3) (- 4 3) (+ 3 1))) -> 4