让我的lisp代码更健壮

时间:2009-11-21 03:03:04

标签: lisp common-lisp

亲爱的,我现在有一个初步的宏

(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进行此扩展,但不知道如何,所以我真的需要你的想法如何让这个扩展工作。

非常感谢。

2 个答案:

答案 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-listelse-list,具体取决于then?标志,当关键字{{1}时会更改找到了}和:then。作为奖励,支持多个then / else关键字,默认情况下假定为:else