如何使用& key和& rest in cl-defun togather?

时间:2016-04-14 12:28:05

标签: function elisp

我有这样的功能

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

参数cdenil 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) 

2 个答案:

答案 0 :(得分:1)

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

Iow您通过&rest获得的列表包含所有关键字。因此,如果您不想要它们,则必须手动放下它们(此时您最好不要使用&key)。

答案 1 :(得分:1)

(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的建议,我想出了这个。我不是很擅长宏,它有用吗?