亲爱的,我现在有一个初步的宏
(defmacro key-if(test &key then else)
`(cond (,test
,then)
(t,else)))
现在它正确地用作
> (key-if (> 3 1) :then 'ok)
OK
> (key-if (< 5 3) :else 'ok)
OK
> (key-if (> 3 1) :else 'oops)
NIL
> (key-if (> 3 1) :else 'oops :then 'ok)
OK
现在我想扩展一下,这意味着我希望有:then
或:else
(关键字)后跟任意数量的参数,所以它会像
> (key-if (> 3 1) :then)
NIL
> (key-if (> 3 1) :else 'oops :then (print 'hi) 'ok)
HI
OK
所以我现在坚持这一点,我对Lisp宏有点新意。我可以考虑使用&rest
进行此扩展,但不知道如何,所以我真的需要你的想法如何让这个扩展工作。
非常感谢。
答案 0 :(得分:2)
我假设你正在使用一些Common Lisp实现。
the standard lambda lists used by DEFMACRO不直接支持这种参数解析方式。你是正确的认为你必须自己解析参数(你可以使用(test &rest keys-and-forms)
来捕获TEST,但是提取:ELSE和:那么部分将取决于你。
我不是超级(Common-)Lisper,但你在这里发明的语法似乎非常不恰当。第一个提示是宏lambda列表不支持你想要的。此外,已经有类型的原始字符长度相同或更短的标准替代品(通过使用Emacs中的paredit等结构编辑器可以减少打字开销。)
(key-if blah :then foo bar) ; multiple then, no else
(when blah foo bar) ; standard
(key-if blah :else baz quux) ; no then, multiple else
(unless blah baz quux) ; standard
(key-if blah :then foo :else baz quux) ; one then, multiple else
(if blah foo (progn baz quux)) ; standard
(cond (blah foo) (t baz quux))
(key-if blah :then foo bar :else baz) ; multiple then, one else
(if blah (progn foo bar) baz) ; standard
(cond (blah foo bar) (t baz))
(key-if blah :then foo bar :else baz quux) ; multiple then, multiple else
(if blah (progn foo bar) (progn baz quux)) ; standard
(cond (blah foo bar) (t baz quux)) ; even shorter
(key-if blah :else baz quux :then foo bar) ; multiple else before multiple then
(if (not blah) (progn baz quux) (progn foo bar)) ; only standard variation that is longer
(cond ((not blah) baz quux) (t foo bar) ; standard, shorter
Common Lisp宏是非常强大的代码模板引擎,但这种特殊用法让我觉得你对Common Lisp中的标准样式和习惯用语不太满意。无论您使用哪种语言,通过采用“本地”风格和习语而不是使用您更熟悉的其他语言的风格和习语,“顺其自然”几乎总是一个好主意。
答案 1 :(得分:2)
&amp; KEY机制仅适用于对。所以你只有两条路径:继续使用对(你可以使用then和else参数的列表,把这些列表放到PROGN中),或者你自己在&amp; REST上使用解析器。
以下是&amp; REST的解析器:
(defmacro keyif (test &rest rest)
(loop with then? = t
for arg in rest
if (eq arg :then) do (setf then? t)
else if (eq arg :else) do (setf then? nil)
else if then? collect arg into then-list
else collect arg into else-list
finally (return `(if ,test (progn ,@then-list) (progn ,@else-list)))))
它在rest
列表上循环,将args收集到then-list
或else-list
,具体取决于then?
标志,当关键字{{1}时会更改找到了}和:then
。作为奖励,支持多个then / else关键字,默认情况下假定为:else
。