【问题标题】:Two questions about Racket macro关于 Racket 宏的两个问题
【发布时间】:2017-01-10 08:32:51
【问题描述】:
  1. 关于卫生宏

    我不完全了解卫生宏的工作原理。这是两个例子。
    第一个是:


    #lang racket
    (define-syntax (g stx)
      (syntax-case stx ()
        ([_ arg]
         #'(display arg))))

    (let ([display 1])
      (g 3))

这很好,但这个:


    #lang racket
    (define-syntax (g stx)
      (syntax-case stx ()
        ([_ arg]
         #'(display arg))))

    (define display 1)

    (g 3)

会引发异常。如何解释这两种情况的区别?

  1. 如何定义这样的宏

    我想定义一个宏来允许在球拍中使用匿名递归函数。 这个不起作用,因为模块中没有定义 recur:


    #lang racket
    (define Z
      (λ(recur)
        ((λ(x) (recur (λ(y) (x x) y)))
         (λ(x) (recur (λ(y) (x x) y))))))

    (define-syntax-rule (R proc)
      (Z (λ(recur) proc)))

    ((R (λ(n)
          (if [= n 1]
              1
              (* n (recur (- n 1)))))) 3)

如何做到这一点?

【问题讨论】:

  • 这个问题如果分成两个独立的问题会更好。

标签: macros scheme racket


【解决方案1】:

要回答您的第一个问题,您在这里忘记的事情是,当您像这样执行模块级别 define 时,该定义是针对整个模块的。所以,理论上你可以这样写你的第二个代码块:

#lang racket
(let ([display 1])
  (define-syntax (g stx)
    (syntax-case stx ()
      ([_ arg]
       #'(display arg))))

  (g 3))

现在你得到错误的原因是有道理的,因为你的宏中的显示绑定到1,这不是一个函数。

长话短说,将卫生视为词汇范围。当您定义您的宏时,无论display 是什么,它都会是什么。 (这与其他语言中的宏相反,当您调用(或真正展开)宏时,无论display 绑定到什么,它都会是。

现在,为了回答你的第二个问题,我很抱歉,但我不清楚你想在这里问什么。如果你能把它清理一下,那么我可以填写这部分答案。

【讨论】:

  • 感谢您的回答,对您有很大帮助。正如 Sylwester 所说,我真正想做的是在第二个问题中打破 hygene。
【解决方案2】:

所以你想打破卫生?您需要获取 recur 以获取原始表单的词法上下文,以便 recur 将被视为相同的标识符。您可以使用datum->syntax 执行此操作,结果可能如下所示:

(define-syntax (recur-λ stx)
  (syntax-case stx ()
    [(_ args body ...)
     (with-syntax ([recur-stx (datum->syntax stx 'recur)])
       #'(Z (λ (recur-stx)
              (λ args body ...))))]))

现在只要你的 args 或 body 中的嵌套引入 recur 就可以了:

; multiple argument recursion
(define Z
  (λ (f)
    ((λ (g) (g g))
     (λ (g)
       (f (λ args (apply (g g) args)))))))


; ackerman    
((recur-λ (m n)
     (cond
       ((= m 0) (+ n 1))
       ((= n 0) (recur (- m 1) 1))
       (else (recur (- m 1) (recur m (- n 1))))))
 3
 6)
; ==> 509

如果您将recur 设为参数,它将不起作用:

((recur-λ (recur) (recur 1)) 1)
; ==> error: recur not a procedure

当然,如果您进行嵌套绑定:

((recur-λ (a) 
   (define recur a) 
   (recur 1))
 1)
; ==> error: recur not a procedure

当然你也可以单步调试宏扩展器,它会告诉你它做了这样的事情:

(expand-once 
 #'(recur-λ (m n)
     (cond
       ((= m 0) (+ n 1))
       ((= n 0) (recur (- m 1) 1))
       (else (recur (- m 1) (recur m (- n 1)))))))

; ==>
; #'(Z
;    (λ (recur)
;      (λ (m n)
;        (cond
;         ((= m 0) (+ n 1))
;         ((= n 0) (recur (- m 1) 1))
;         (else (recur (- m 1) (recur m (- n 1))))))))

【讨论】:

    猜你喜欢
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-31
    • 2013-02-20
    • 1970-01-01
    • 2012-04-01
    • 1970-01-01
    相关资源
    最近更新 更多