【问题标题】:What are some things that you've used Scheme macros for? [closed]您使用过 Scheme 宏的一些事情是什么? [关闭]
【发布时间】:2010-09-19 17:29:21
【问题描述】:

许多宏示例似乎是关于隐藏 lambda,例如在 CL 中打开文件。我正在寻找宏的一些更奇特的用途,特别是在 PLT 方案中。我想了解一下何时考虑使用宏还是使用函数。

【问题讨论】:

标签: lisp scheme macros racket


【解决方案1】:

需要宏来实现新的控制结构和新的绑定结构。

因此在http://planet.plt-scheme.org 寻找这些类型的结构。 在 PLaneT,您可以同时浏览文档和代码。

新控制结构示例:

http://planet.plt-scheme.org/package-source/soegaard/control.plt/2/0/planet-docs/manual/index.html

要查找新绑定形式的示例,请查找以“with-”开头的宏。 PLaneT 的 math.plt 中也有一个有用的示例。

  ; Within a (with-modulus n form1 ...) the return values of
  ; the arithmetival operations +, -, * and ^ are automatically
  ; reduced modulo n. Furthermore (mod x)=(modulo x n) and
  ; (inv x)=(inverse x n).

  ; Example: (with-modulus 3 (^ 2 4)) ==> 1

  (define-syntax (with-modulus stx)
    (syntax-case stx ()
      [(with-modulus e form ...)
       (with-syntax ([+   (datum->syntax-object (syntax with-modulus) '+)]
                     [-   (datum->syntax-object (syntax with-modulus) '-)]
                     [*   (datum->syntax-object (syntax with-modulus) '*)]
                     [^   (datum->syntax-object (syntax with-modulus) '^)]
                     [mod (datum->syntax-object (syntax with-modulus) 'mod)]
                     [inv (datum->syntax-object (syntax with-modulus) 'inv)])
         (syntax (let* ([n e]
                        [mod    (lambda (x)   (modulo x n))]
                        [inv    (lambda (x)   (inverse x n))]
                        [+      (compose mod +)]
                        [-      (compose mod -)]
                        [*      (compose mod *)]
                        [square (lambda (x) (* x x))]
                        [^      (rec ^ (lambda (a b)
                                         (cond
                                           [(= b 0)   1]
                                           [(even? b) (square (^ a (/ b 2)))]
                                           [else      (* a (^ a (sub1 b)))])))])
                   form ...)))]))

【讨论】:

    【解决方案2】:

    我只使用 Scheme 宏 (define-syntax) 来处理诸如更好的 lambda 语法之类的小事:

    (define-syntax [: x]
      (syntax-case x ()
        ([src-: e es ...]
         (syntax-case (datum->syntax-object #'src-: '_) ()
           (_ #'(lambda (_) (e es ...)))))))
    

    让你写作

    [: / _ 2]  ; <-- much better than (lambda (x) (/ x 2))
    

    Dan Friedman 使用宏实现了令人费解的 OO:http://www.cs.indiana.edu/~dfried/ooo.pdf

    但老实说,我定义的所有 有用 宏都是从 Paul Graham's On Lisp 窃取的,并且通常使用 defmacro(PLT 方案中的 define-macro)更容易编写。例如,aifdefine-syntax 相当难看。

    (define-syntax (aif x)
      (syntax-case x ()
        [(src-aif test then else)
         (syntax-case (datum->syntax-object (syntax src-aif) '_) ()
           [_ (syntax (let ([_ test]) (if (and _ (not (null? _))) then else)))])]))
    

    define-syntax 很奇怪,因为它只适用于非常简单的宏,您很高兴无法捕获变量;以及非常复杂的宏 DSL,您很高兴无法轻松捕获变量。在第一种情况下,您想编写代码而不考虑它,而在第二种情况下,您已经对 DSL 进行了足够的考虑,因此您愿意用不是 Scheme 的 syntax-rules/syntax-case 语言编写其中的一部分为了避免神秘的错误。


    但我在Scheme 中没有太多使用宏。 Idiomatic Sc​​heme 非常实用,以至于很多时候您只想编写一个函数式程序,然后隐藏一些 lambda。我上了函数式训练,现在相信如果你有一种惰性语言或 lambda 的良好语法,即使这样也没有必要,所以宏在纯函数式风格中并不是那么有用。

    所以我推荐Practical Common LispOn Lisp。如果您想使用 PLT Scheme,我认为他们的大多数 defmacro 宏都可以与 define-macro 一起使用。或者只使用 Common Lisp。

    【讨论】:

    • 如果你使用语法参数,aif 定义起来会更干净(而且卫生!):`(define-syntax-parameter it #f) (define-syntax aif (syntax-parser [ (_ test then else) #'(let ([result test]) (syntax-parameterize ([it (make-rename-transformer result)]) (if result then else)))]))
    【解决方案3】:

    我将开始回答最后一个问题。何时使用宏而不是函数。宏做函数不能做的事情,函数做宏不能做的事情,所以很难混合它们,但让我们更深入。

    当你想要评估参数时使用函数,当你想要不评估参数时使用宏。这不是很有用,是吗?当你想以不同的方式写东西,当你看到一个模式并且你想抽象时,你会使用宏。例如:我为 foo 的不同值和类似的主体定义了三个函数,称为 foo-create、foo-process 和 foo-destroy,其中唯一的变化是 foo。有一个模式,但函数级别太高,所以你创建一个宏。

    根据我的拙见,Scheme 中的宏与其他 Lisp 中的使用一样多,例如 Common Lisp 或 Clojure。我想这证明了卫生宏可能不是一个好主意,在这里我不同意 Paul Graham 的原因。这不是因为有时您想弄脏(不卫生),而是因为卫生宏最终变得复杂或令人费解。

    【讨论】:

    • “因为卫生宏最终变得复杂或令人费解”——我不认为这是因为卫生本身;更有可能是因为 Scheme hygienic 宏是“高级”的,并且必须用 Scheme 以外的语言编写(就我而言,它比 Scheme 本身方便得多!)这就是为什么我喜欢 Common Lisp 并且仍然使用在 Scheme 中定义宏,即使 R5RS 有这种尴尬的语法规则... :-)
    【解决方案4】:

    Practical Common Lisp,作者 Peter Seibel,对宏有很好的介绍。在 Lisp 上,由 Paul Graham 撰写,可能是更复杂示例的良好来源。另外,看看 Common Lisp 中的内置宏。

    【讨论】:

    • 方案宏 != Common Lisp 宏
    【解决方案5】:

    Automata via Macros 论文介绍了通过 Scheme 中的宏实现有限状态机的函数式编程珍珠。

    本书The Reasoned Schemer 以完整的基于宏的miniKanren 实现结束,miniKanren 是书中使用的逻辑编程语言。 This paper 比书中更正式、更简洁地介绍了 miniKanren 及其实现。

    【讨论】:

    • 自动机通过宏链接是纯金。我有一个巨大的啊哈!读它的那一刻。来自我的 +1。
    【解决方案6】:

    Common Lisp 的宏with-slots 是一个不是变相的 lambda 形式的更高级宏的示例,它使对象插槽访问看起来像普通的变量访问:

    (with-slots (state door) car
      (when (eq state :stopped)
        (setq state :driving-around)
        (setq door :closed)))
    

    请注意,这与将插槽值绑定到局部变量并访问它们不同,因为 with-slots 允许您通过 SETQ 更改插槽并立即查看外部更改。 p>

    【讨论】:

    • 确实不推荐使用 setq 来支持 setf。如果像这样的示例使用 setf,那么对于新的(常见的)lispers 来说就不会那么混乱了。
    【解决方案7】:

    当我以前在我的手掌上做很多计划时,我有一个 curry 宏。这很方便。

    【讨论】:

      【解决方案8】:

      方案宏可让您添加原始语言作者未包含的功能;这就是宏背后的全部理念。

      这是一个小例子:PLT Scheme 提供了一种用于编写演示文稿的语言,称为幻灯片。我使用宏将幻灯片编号与幻灯片关联起来,以便更轻松地管理它们。

      【讨论】:

        【解决方案9】:

        我编写了一个提供中缀语法的宏。没有什么太花哨的;没有优先权。虽然我通常对前缀语法没问题,但我更喜欢 的中缀。

        【讨论】:

          【解决方案10】:

          当程序不够用时,我会使用它们。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2010-10-29
            • 2011-02-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多