13

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

4

10 回答 10

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 ...)))]))
于 2008-11-04T18:56:56.263 回答
7

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

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

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

于 2008-10-29T07:42:24.617 回答
7

我只将 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 的 On Lisp中偷来的,并且通常更容易编写defmacrodefine-macro在 PLT 方案中)。例如,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。

于 2008-10-29T15:23:48.383 回答
4

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

于 2008-10-29T11:27:13.463 回答
4

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

本书The Reasoned Schemer以一个完整的基于宏的 miniKanren 实现作为结尾,miniKanren 是书中使用的逻辑编程语言。与书中相比,本文更正式、更简洁地介绍了 miniKanren 及其实现。

于 2008-12-01T07:35:11.600 回答
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 更改插槽并立即查看外部更改。

于 2008-10-29T18:36:05.623 回答
1

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

于 2008-12-01T08:10:22.450 回答
1

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

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

于 2008-12-10T20:33:17.017 回答
1

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

于 2010-06-30T20:42:35.770 回答
0

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

于 2008-12-01T08:00:54.170 回答