【问题标题】:once-only lisp macro, is my implementation correct?一次性 lisp 宏,我的实现是否正确?
【发布时间】:2014-04-22 23:33:09
【问题描述】:

我正在尝试从 Peter Seibel 的《实用通用 Lisp》一书中学习 Lisp。在chapter 8 : "Macros: Defining your own" 中,我遇到了这个只有一次的宏。在该页面的底部,给出了一个实现。现在这对我来说是一个非常复杂的宏,所以我看到了this question on stackoverflow,那里有一些很好的解释。

但是,即使我(仍然)还没有完全理解宏,我也理解了它的用途。所以我尝试编写自己的实现:

(defmacro my-once-only ((&rest names) &body body)
  (let
    (
      (gensyms (loop for n in names collect (gensym)))
    )

    `(list 'let
      (list ,@(loop for n in names for g in gensyms collect `(list ',g ,n)))

      (let
        ,(loop for n in names for g in gensyms collect `(,n ',g))

        ,@body))))

(如果我没有遵循标准的 lisp 缩进约定,请原谅我,我试图以某种方式缩进代码,以便我可以理解去哪里,因为我是新手)

我测试这个宏的方式与我链接的那一章中描述的方式大致相同,即。使用 (random 100) 之类的参数调用函数,这样如果对它们进行两次评估,结果将是错误的。我还通过macroexpand/macroexpand-1 扩展了我的宏(以及我使用它的宏),这似乎也是正确的。

所以我想知道我的实现是否正确?还是我遗漏了什么(我可能认为)......

【问题讨论】:

    标签: macros lisp common-lisp practical-common-lisp


    【解决方案1】:

    让我们实际对这两个实现进行宏展开,看看它们有何不同:

    * (macroexpand '(once-only (foo bar) (+ foo bar)))
    (LET ((#:G619 (GENSYM)) (#:G620 (GENSYM)))
      `(LET ((,#:G619 ,FOO) (,#:G620 ,BAR))
         ,(LET ((FOO #:G619) (BAR #:G620))
            (+ FOO BAR))))
    
    * (macroexpand '(my-once-only (foo bar) (+ foo bar)))
    (LIST 'LET (LIST (LIST '#:G621 FOO) (LIST '#:G622 BAR))
          (LET ((FOO '#:G621) (BAR '#:G622))
            (+ FOO BAR)))
    

    让我们将您的宏扩展重写为更易于 Lisper 阅读的内容:

    `(LET ((#:G621 ,FOO) (#:G622 ,BAR))
       ,(LET ((FOO '#:G621) (BAR '#:G622))
          (+ FOO BAR)))
    

    请注意,您的版本缺少带有附加 gensym 的间接性。这意味着每次调用外部宏(使用my-once-only 的宏)每次都使用相同的符号。如果您的宏调用嵌套(例如,您在另一个使用外部宏的主体内使用外部宏),符号将发生冲突。

    【讨论】:

    • 感谢您的解释。没有想过我的外部宏furthur被另一个内部使用的情况。
    猜你喜欢
    • 2012-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-17
    • 2011-03-28
    • 1970-01-01
    • 2013-11-09
    • 2013-05-06
    相关资源
    最近更新 更多