你如何写一个MIT Scheme宏来返回一个lambda表单?

时间:2011-10-12 17:01:11

标签: macros lisp scheme sicp

我很困惑,试图在MIT Scheme中创建相当于这个简单的(在Common Lisp中)宏:

(defmacro funcify (exp)
    `(lambda (x) ,exp))

这是一个简单的个人项目,一个基于第二次SICP讲座中建立的功能的数值方程求解器。我不在乎这个宏不是“安全”或“卫生”,或者如果exp引用除'x之外的任何符号,它将捕获变量。我希望能够写

(solv '(* 60 x) '(* 90 (- x 1)))

其中solv是:

(define (solv lh-exp rh-exp)
    (solve (funcify lh-exp) (funcify rh-exp)))

而不必输入

(solve (lambda (x) (* 60 x)) (lambda (x) (* 90 (- x 1))))

但无法弄清楚如何使用MIT Scheme语法规则来做到这一点。

我试过这个,但它不起作用:

(define-syntax funcify
  (syntax-rules ()
    ((funcify y) (lambda (x) y))))
;Value: funcify

(funcify x)
;Value 17: #[compound-procedure 17]

((funcify x) 10)
;Unbound variable: x

我尝试过其他可能不值得提及eval的事情,但无济于事。

另外,在Scheme的宏系统上引用了很好的教程(不是引用),从小的简单示例开始并构建,有充分的注释,特别是展示了如何转换backquote-comma样式的LISP宏(这对我来说很高直观)到Scheme的语法宏系统会很棒。

2 个答案:

答案 0 :(得分:7)

无法在syntax-rules中完成。故事结束。

在输出表达式中注入任意标识符(x)需要打破卫生,syntax-rules不提供任何破坏卫生的方法。您需要使用较低级别的宏系统来执行此操作。 MIT Scheme使用显式重命名(请参阅Matthias Benkard的回答),但对于使用syntax-case的其他Scheme实现,您可以这样做:

(define-syntax funcify
  (lambda (stx)
    (syntax-case stx ()
      ((_ body)
       (with-syntax ((x (datum->syntax stx 'x)))
         #'(lambda (x)
             body))))))

密钥是(datum->syntax stx 'x)位,它注入符号x,就好像它位于funcify调用的语法上下文中一样。

顺便说一下,您的solv也必须是一个宏,而不是一个过程,但至少它可以是一个syntax-rules宏:

(define-syntax solv
  (syntax-rules ()
    ((_ lhs rhs) (solve (funcify lhs) (funcify rhs)))))

答案 1 :(得分:4)

您可以使用explicit-renaming macrosdefmacro基本相同。唯一重要的区别是您必须自己解构输入表单:

(define-syntax funcify
  (er-macro-transformer
    (lambda (form rename cmp)
      (let ((exp (cadr form)))
        `(,(rename 'lambda) (x) ,exp)))))