Очень маленький пример, показывающий, что происходит не так:
(defun test-key-rest (&rest args &key a (b t))
(list 'args args 'a a 'b b))
(test-key-rest :a 1 :b 2); => (ARGS (:A 1 :B 2) A 1 B 2)
(test-key-rest :a 1 :b 2 "rest now?");;; Error: The passed key "rest now?" is not defined for this function
(test-key-rest :a 1 :b 2 :c 3);;; Error: The passed key :C is not defined for this function
Можно, возможно, использовать & allow-other-keys , но я думаю, что это будет грязно.Я вспомнил, как читал об этой ситуации в Practical Common Lisp , где Питер Сейбел пишет (выделение мое):
Две другие комбинации: либо необязательные, либо и остальные параметрыв сочетании с параметрами & key может привести к несколько неожиданному поведению.
Я предлагаю разделить два списка аргументов. destructuring-bind упрощает:
(defun test-two-arg-lists (keys &rest args)
(destructuring-bind (&key (a nil) (b t)) keys
(list 'a a 'b b 'args args)))
(test-two-arg-lists (list :a 1) "more" "please"); => (A 1 B T ARGS ("more" "please"))
Но я (и я предполагаю, что другие) не хотят создавать этот первый список аргументов ключевого слова, поэтому давайте сделаем так, чтобы он оценил егоаргументы, как мы и ожидали, с помощью макроса:
(defmacro test-two-nice (keys &rest args)
`(test-two-arg-lists (list ,@keys) ,@args))
(test-two-nice (:a 1) "more" "please"); => (A 1 B T ARGS ("more" "please"))
Итак, чтобы собрать все вместе:
(defun create-symbol-fn (keys &rest objects)
"Creates a symbol from the objects."
(destructuring-bind (&key intern (package *package*)) keys
(let ((arg-string (format nil "~{~A~}" objects)))
(if intern
(values (intern arg-string package))
(make-symbol arg-string)))))
(defmacro create-symbol (keys &rest objects)
`(create-symbol-fn (list ,@keys) ,@objects))
(create-symbol (:intern nil) 'a 'b 'c 'd); => #:ABCD