如何在 cl-defun 中同时使用 &key 和 &rest?

How to use &key and &rest in a cl-defun togather?

我有这样的功能

(cl-defun foo (a b c d e &rest f)
  nil)

参数 cde nil 80% 的时间。

为了让它看起来更好看,我这样做了:

(cl-defun foo (a b &rest f &key c d e &allow-other-keys)
  nil)

不提供cde也可以。

但是,如果使用其中之一,f 会得到一个错误的参数。 例如:

(foo 1 2 :c 6 3 4 5)
;; ==> expected: a=1, b=2, c=6, f= (3 4 5)
;; ==> real case: a=1, b=2, c=6, f= (:c 6 3 4 5) 

您看到的行为是 CommonLisp 指定的行为(实际上我不确定您的调用 (foo 1 2 :c 6 3 4 5) 在 Common-Lisp 中是否有效,因为我认为它会将 3 和 5 视为退化 关键字5 关键字缺少值)。

IOW 您通过 &rest 获得的列表包括所有关键字。因此,如果您不想要它们,则必须手动删除它们(此时您通常最好根本不使用 &key)。

从列表 f 中删除键 c d e 的示例:

(dolist (key '(c d e))
  (cl-remf f key))
(cl-defmacro foo2 (a b &rest f &key c d e &allow-other-keys)
  (let (key rest)
    (dolist (elt f)
      (if (memq elt '(:c :d :e))
          (setq key elt)
        (if key
            (progn
              (set (intern-soft (string-remove-prefix ":" (symbol-name key))) elt)
              (setq key nil))
          (push elt rest))))
    (setq rest (nreverse rest))

    `(foo ,a ,b ,c ,d ,e ,@rest)))

(pp-macroexpand-expression '(foo2 1 2 :c 3 :d 4 :e 5 6 7 8 9))
;; ==> (foo 1 2 3 4 5 6 7 8 9)
(pp-macroexpand-expression '(foo2 1 2 3 4 5 6))
;; ==> (foo 1 2 nil nil nil 3 4 5 6)
(pp-macroexpand-expression '(foo2 1 2 3 4 5 6 :c 7 :d 8 :e 9))
;; ==> (foo 1 2 7 8 9 3 4 5 6)
(pp-macroexpand-expression '(foo2 1 2 3 :c 4 5 :d 6 7 :e 8 9))
;; Extreme case
;; ==> (foo 1 2 4 6 8 3 5 7 9)

在@Stefan 的建议下,我想到了这个。我不太擅长微距,可以吗?