宏定义时将键视为真

时间:2018-03-10 20:11:07

标签: macros common-lisp elisp

在elisp中使用cl-defmacro时,是否可以将密钥视为真?例如,

(cl-defmacro mac (&key a b c)
  `(,@(if a "a" (if b "b" "c"))))

(mac :a)
"c"

我是否可以将其评估为“a”而无需向:a提供(mac :a t之类的值。我不知道相同的语义是否适用于common-lisp

1 个答案:

答案 0 :(得分:3)

使用基本语言无法做到这一点。可以定义一个defun之类的宏来执行此操作。您是否希望允许将这些设置为true,或者您是否希望提供除t以外的值(或nil是否遗漏)?第一种情况可以通过编写一些宏来完成。第二种情况不能很好地完成(特别是(mac :a :b)a <- t; b <- ta <- :b?)

以下是您可以为Common Lisp撰写的内容:

(defmacro keyset-bind (keys-and-vars form &body body)
  (let (syms vars
        (keyv (gensym "KEY")))
    (loop for key in keys-and-vars
      for sym = (if (consp key)
                    (car key)
                    (intern (symbol-name key) "KEYWORD"))
      for var = (if (consp key) (cadr key) key)
      collect sym into symst
      collect var into varst
      finally (setf syms symst vars varst))
    `(let ,vars
        (loop for ,keyv in ,form
          do (case ,keyv
               ,@(loop for sym in syms
                   for var in vars
                   collect `((,sym) (setf ,var t)))
               (t (error "unrecognised keyword ~a" ,keyv))))
        ,@body)))

然后您就可以使用它:

(defun mac (&rest switches)
  (keyset-bind (a b (:t foo)) switches
    (list a b foo)))

CL-USER> (mac :a)
(T NIL NIL)
CL-USER> (mac :b :t)
(NIL NIL T)
CL-USER> (mac :baz)
;; Error ...

可以用这种方式编写另一个defun来接受例如&switch参数类型,但是你需要决定如何使它与其他参数类型和关键字进行交互{{1} }和&allow-other-keys。通常所有这些都是一个坏主意,并且不鼓励与上述任何类型的混合(或甚至使用),因为它使得难以知道不熟悉的函数调用是否将被解释为普通关键字或关键字集。如果你想要一种支持这种事情的语言,请查看perl6,其中关键字是键 - 值对类型,:allow-other-keys tkey => true有特殊语法。