我在guile中有一个宏let--(比如让*使用lambdas):
(define (let-make-lambdas pairs body)
(if (null? pairs)
`((lambda () ,@body))
`((lambda (,(caar pairs))
,(let-make-lambdas (cdr pairs) body))
,(cadar pairs))))
(define-macro (let-- pairs . body)
(let-make-lambdas pairs body))
当我使用外部函数进行代码生成时,它工作正常,但下面的代码(只是一个宏)不起作用:
(define-macro (let-- pairs . body)
(if (null? pairs)
`((lambda () ,@body))
`((lambda (,(caar pairs))
,(let-- (cdr pairs) body))
,(cadar pairs))))
为什么?
答案 0 :(得分:3)
在第二个中,你不想要
,(let-- (cdr pairs) body)
而是
(let-- ,(cdr pairs) ,@body)
也就是说,你的直接宏实现应该是
(define-macro (let-- pairs . body)
(if (null? pairs)
`((lambda () ,@body))
`((lambda (,(caar pairs))
(let-- ,(cdr pairs) ,@body))
,(cadar pairs))))
您不希望在宏扩展时评估内部(let-- ...)
;它是应该生成的源的一部分。 (当然,它很快就会被宏观扩展。)为了突出这一点,考虑一个转动的宏
(plus a b c d)
进入
(+ a (+ b (+ c d)))
需要像
一样扩展(+ ,(car args) (plus ,@(cdr args)))
但不是
(+ ,(car args) ,(plus (cdr args)))
因为后者会尝试评估(plus '(b c d))
,这将无效。
答案 1 :(得分:2)
我认为约书亚为你解决了问题。我只想指出Scheme
标准使用syntax-rules
和syntax-case
。它可能与syntax-rules
:
;; make let* with lambdas
(define-syntax let--
(syntax-rules ()
;; base case, last pair
((let-- ((key1 value1)) . body)
((lambda (key1) . body ) value1))
;; default case, several
((let-- ((key1 value1) . kv-pairs) . body)
((lambda (key1) (let-- kv-pairs . body)) value1))))
(let-- ((a 'a) (b a) (c b)) (list a b c)) ; ==> (a a a)
答案 2 :(得分:1)
这是一个有效的Common Lisp版本:
(defmacro let1-- (pairs . body)
(if (null pairs)
`((lambda () ,@body))
`((lambda (,(caar pairs))
(let-- ,(cdr pairs) . ,body))
,(cadar pairs))))
> (macroexpand '(let1-- ((a 1) (b 2)) (+ b a)))
((LAMBDA (A) (LET-- ((B 2)) (+ B A))) 1) ;
T
> (let1-- ((a 1) (b 2)) (+ b a))
3
相应的Scheme版本,我想,
(define-macro (let-- pairs . body)
(if (null? pairs)
`((lambda () ,@body))
`((lambda (,(caar pairs))
(let-- ,(cdr pairs) . ,body))
,(cadar pairs))))