Scheme 球拍宏观扩张中的评价形式
这是一个常见的Lisp宏和测试函数Scheme 球拍宏观扩张中的评价形式,scheme,lisp,racket,Scheme,Lisp,Racket,这是一个常见的Lisp宏和测试函数 (defmacro test (body) `(let ,(mapcar #'(lambda (s) `(,s ,(char-code (char-downcase (char (symbol-name s) 0))))) '(a b)) ,body)) (test (+ a b)) 扩展到 (let ((a 97) (b 98)) (+ a b)) 评估时给出195 在球拍里试着这么
(defmacro test (body)
`(let ,(mapcar #'(lambda (s)
`(,s ,(char-code (char-downcase (char (symbol-name s) 0)))))
'(a b))
,body))
(test (+ a b))
扩展到
(let ((a 97) (b 98))
(+ a b))
评估时给出195
在球拍里试着这么做
(define-syntax (test stx)
(syntax-case stx ()
[(_ body)
#`(let #,(map (lambda (x)
(list x
(char->integer (car (string->list (symbol->string x))))))
'(a b))
body)]))
(test (+ a b))
运行宏扩展器时,宏窗体将扩展为:
(let ((a 97) (b 98)) (+ a b))))
这正是我想要的
但它在以下方面失败了:
a: unbound identifier in context..
禁用宏隐藏会生成一个以以下内容结尾的表单:
(#%app:35
call-with-values:35
(lambda:35 ()
(let-values:36 (((a:37) (quote 97)) ((b:37) (quote 98)))
(#%app:38 + (#%top . a) b)))
(print-values:35)))
我不明白为什么我漂亮的扩展(let((a97)(b98))(+ab))
不起作用,我对(#%top.a)。。。我想知道它是否在试图找到一个叫做“a”的函数?
当我将扩展的表单复制到REPL中时,它会工作
我很感激你的帮助 球拍有卫生宏。考虑:
(define-syntax-rule (or a b)
(let ([a-val a])
(if a-val a-val b)))
然后:
将大致扩展到:
(let ([a-val 1])
(let ([a-val2 #f])
(if a-val2 a-val2 a-val)))
其计算结果为1
。如果宏不卫生,则会导致被认为不正确的#f
请注意,a-val
自动重命名为a-val2
,以避免冲突。你的案子也是这样
解决本例问题的一种方法是为生成的标识符提供正确的上下文,以便宏扩展器理解它们应该引用相同的变量
(define-syntax (test stx)
(syntax-case stx ()
[(_ body)
#`(let #,(map (lambda (x)
(list (datum->syntax stx x) ; <-- change here
(char->integer (car (string->list (symbol->string x))))))
'(a b))
body)]))
(test (+ a b))
(定义语法(测试stx)
(语法大小写stx()
[(uu)正文)
#`(让#,,(图(λ(x))
(列表(基准->语法stx x);整数(car(字符串->列表(符号->字符串x‘‘‘‘‘)’)
"(a)(b)
(正文))
(测试(+AB))
作为对应项(这是正确的答案),我认为有必要思考一下为什么您的测试宏在CL中有问题,为什么做类似事情的宏完全有问题
给定您的测试
宏,想象一些用户正在查看此代码:
(let ((a 1) (b 2))
(test (+ a b)))
嗯,我不知道你的情况,但我希望发生的是a
和b
内部测试是我刚刚绑定的a
和b
。当然,事实并非如此
嗯,也许test
的文档非常详细地描述了它绑定了两个变量,这就是我应该期望的。当然,也有宏可以做到这一点,而且在哪里是好的:
(defmacro awhen (test &body forms)
`(let ((it ,test))
(when ,it ,@forms)))
现在:
(awhen (find-exploder thing)
(explode it))
这一切都很好,因为awhen
的文档会说它将it
绑定到其主体中的测试结果
但现在请考虑这个代码>或代码>宏:从另一个答案中窃取:
(defmacro vel (a b)
`(let ((a-val ,a))
(if a-val a-val ,b)))
这是一场灾难。它“起作用”,但它根本不起作用:
> (let ((a-val 3))
(vel nil a-val))
nil
现在这不仅仅是在你的测试宏的方式上令人惊讶:它是错误的
相反,您必须在CL中这样编写vel
:
(defmacro vel (a b)
(let ((a-val-name (make-symbol "A-VAL")))
`(let ((,a-val-name ,a))
(if ,a-val-name ,a-val-name ,b))))
(当然,您可以使用gensym
而不是makesymbol
,我想大多数人都会这么做。)
现在呢
> (let ((a-val 3))
(vel nil a-val))
3
> (with-char-codes (a b)
(+ a b))
195
如你所料
这一切都是因为CL宏系统不卫生——它依赖于您来确保名称等内容不会冲突。在CL中,您必须稍微费劲地编写宏,这些宏在许多情况下都是正确的。另一方面,Racket宏系统是卫生的:默认情况下,它将确保名称(和其他内容)不会冲突。在Racket(和Scheme)中,您必须不遗余力地编写宏,这些宏要么不正确,要么做一些稍微出乎意料的事情,比如引入从代码中可见的绑定来使用宏
请注意,我并没有表达对这两种宏方法的偏好:我花了大部分时间编写CL,我对它的宏系统非常满意。最近我写了更多的《球拍》,我对它的宏观系统也很满意,尽管我发现它更难理解
最后,这里是一个在使用中不太令人惊讶的宏变体(此代码中几乎所有的杂音都是健全性检查,语法分析
以两个#:失败时
子句的形式支持):
现在呢
> (let ((a-val 3))
(vel nil a-val))
3
> (with-char-codes (a b)
(+ a b))
195