关于卫生宏
我不完全了解宏观工作的卫生程度。这是两个例子 第一个是:
#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)
会引发异常。如何解释这两种情况的区别?
如何定义像这样的宏
我想定义一个宏来允许在球拍中使用匿名递归函数。 这个不起作用,因为模块中没有定义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)
如何实现这一目标?
答案 0 :(得分:3)
要回答你的第一个问题,你在这里忘记的事情就是当你做这样的模块级别define
时,该定义将被绑定到整个模块。所以,你可以理论上写下你的第二个代码块:
#lang racket
(let ([display 1])
(define-syntax (g stx)
(syntax-case stx ()
([_ arg]
#'(display arg))))
(g 3))
现在,为什么会出现错误是有道理的,因为宏中的显示被绑定到1
,这不是一个函数。
长话短说,将卫生视为词汇范围。当您定义宏时,display
绑定的是什么。 (这与其他语言中的宏相反,当你调用(或者真正展开)宏时,display
绑定的是什么,它将是什么
现在,为了回答你的第二个问题,我道歉,但不清楚你在这里要问的是什么。如果你可以稍微清理一下,那么我可以填写这部分答案。
答案 1 :(得分:1)
所以你想打破hygene?您需要让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或正文中的嵌套引入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))))))))