Вот пример того, как вы можете делать то, что хотите.Это довольно простой способ, но он позволяет вам определять функции, которые принимают любое количество аргументов, вместе с нулевым или большим количеством аргументов ключевых слов.Затем есть небольшой батут, который извлекает ключевые слова и их значения из аргументов и вызывает функцию надлежащим образом.
Это не означает, что это код производственного качества: было бы лучше иметь батут, производящий батут.Функция точно знает, какие ключевые слова она искала, например, которые могут быть известны, а не просто «любые ключевые слова».
(defun make-kw-trampoline (fn)
;; Given a function which takes a single rest arg and a bunch of
;; keyword args, return a function which will extract the keywords
;; from a big rest list and call it appropriately
(lambda (&rest args)
(loop for (arg . rest) on args
if (keywordp arg)
if (not (null rest))
collect arg into kws and collect (first rest) into kws
else do (error "Unpaired keyword ~S" arg)
finally (return (apply fn args kws)))))
(defmacro defun/rest/kw (name (rest-arg and-key . kw-specs) &body decls-and-forms)
;; Define a function which can take any number of arguments and zero
;; or more keyword arguments.
(unless (eql and-key '&key)
(error "um"))
(multiple-value-bind (decls forms) (loop for (thing . rest) on decls-and-forms
while (and (consp thing)
(eql (first thing) 'declare))
collect thing into decls
finally (return
(values decls (cons thing rest))))
`(progn
(setf (fdefinition ',name)
(make-kw-trampoline (lambda (,rest-arg &key ,@kw-specs)
,@decls
(block ,name
,@forms))))
',name)))
Так что, если я сейчас определю функцию, подобную этой:
(defun/rest/kw foo (args &key (x 1 xp))
(declare (optimize debug))
(values args x xp))
Тогда я могу назвать это так:
> (foo 1 2 3)
(1 2 3)
1
t
> (foo 1 2 :x 4 3)
(1 2 :x 4 3)
4
t
Обратите внимание, что defun/rest/kw
может не делать то же самое, что defun
: в частности, я думаю, что этого достаточно для правильного определения функции (ине определять его во время компиляции), но компилятор может не осознавать, что функция существует во время компиляции (поэтому могут быть предупреждения), и он также не выполняет никакой специфической для реализации магии.