您使用Scheme宏的一些东西是什么?

时间:2008-10-29 03:12:29

标签: lisp scheme macros racket

宏的许多例子似乎都是关于隐藏lambda,例如CL中的with-open-file。我正在寻找一些更奇特的宏用途,特别是在PLT Scheme中。我想了解何时考虑使用宏与使用函数。

10 个答案:

答案 0 :(得分:8)

为了实现新的控制结构和新的绑定结构,需要宏。

因此在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 ...)))]))

答案 1 :(得分:7)

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

当您希望参数被评估时,您可以使用函数;当您希望参数取消评估时,可以使用宏。这不是很有用,是吗?当您想要以不同的方式编写内容时,当您看到模式并且想要抽象时,可以使用宏。例如:我为foo的不同值定义了三个函数foo-create,foo-process和foo-destroy,并且使用类似的主体,其中唯一的变化是foo。有一个模式,但功能太高,所以你创建一个宏。

在我不起眼的经验中,Scheme中的宏与其他Lisp一样多,比如Common Lisp或Clojure。我想这可能证明卫生宏可能不是一个好主意,在这里我不同意保罗格雷厄姆的原因。这不是因为有时你想要变脏(不卫生),而是因为卫生的宏最终变得复杂或错综复杂。

答案 2 :(得分:6)

我只使用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中偷来的,通常更容易使用defmacrodefine-macro在PLT Scheme中编写)。例如,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,您愿意用syntax-rules / syntax-case语言编写其中的一部分这不是Scheme,以避免神秘的错误。


但我不会在Scheme中使用那么多的宏。惯用方案非常实用,很多时候你只想编写一个功能程序,然后隐藏一些lambda。我上了功能列车,现在相信如果你有一个懒惰的语言或一个良好的lambda语法,即使这是没有必要的,所以宏在纯函数风格中并不是那么有用。

所以我建议Practical Common LispOn Lisp。如果您想使用PLT Scheme,我认为他们的大多数defmacro宏都适用于define-macro。或者只使用Common Lisp。

答案 3 :(得分:3)

实用的Common Lisp,由Peter Seibel撰写,对宏进行了很好的介绍。关于保罗格雷厄姆的Lisp,可能是更复杂的例子的良好来源。另外,请查看Common Lisp中的内置宏。

答案 4 :(得分:3)

Automata via Macros论文提出了一个功能性编程珍珠,通过Scheme中的宏实现有限状态机。

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

答案 5 :(得分:1)

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

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

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

答案 6 :(得分:1)

当我曾经在手掌上做很多计划时,我有一个curry宏。这非常方便。

答案 7 :(得分:1)

Scheme宏允许您添加原始语言作者自己不包含的功能;这就是宏背后的整体哲学。

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

答案 8 :(得分:1)

我编写了一个提供中缀语法的宏。没什么太花哨的;没有优先权。虽然我通常使用前缀语法很好,但我更喜欢infix for&lt;和&gt;。

答案 9 :(得分:0)

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