0

我正在尝试重新编写let*卫生宏,我将它作为普通宏,如果可能的话,我希望将它作为卫生宏。我对这种宏类型没有太多经验。所以我真的很感激帮助。此外,我对工作let*宏的另一种表示不起作用,与卫生宏具有相同的错误。

在职的let*

(define-macro let*1
(lambda (assgn . body)
(let ((loop (gensym))
      (lst (gensym)))
  (let loop ((lst assgn))
     (if (null? lst)
         `(begin ,@body)
         `((lambda (,(caar lst))
             ,(loop (cdr lst)))
           ,(cadar lst)))))))

不卫生let*->错误:lambda:不是标识符:(caar lst)

(define-syntax let*2
(syntax-rules ()
((let*2 (set ...) body ...)
 (let loop ((lst '(set ...)))
   (if (null? lst)
       body ...
       ((lambda ((caar lst))
          (loop (cdr lst)))
        (cadar lst) 1))))))

不工作let*,但也有与第二个相同的错误。

(define-macro let*3
(lambda (assgn . body)
(let ((loop (gensym))
      (lst (gensym)))
  `(let ,loop ((,lst assgn))
     (if (null? ,lst)
         (begin ,@body)
         ((lambda ((caar ,lst))
             (,loop (cdr ,lst)))
           (cadar ,lst)))))))

为这个令人困惑的问题道歉,我已经被这个问题困扰了一段时间,咖啡因不再有用了。

一些测试(我选择符号名称来测试符号捕获(我知道我不必这样做)):

(let*1 ((body 10)
   (lst (+ body 1)))
  (list body lst))

(let*2 ((body 10)
    (lst (+ body 1)))
  (list body lst))

(let*3 ((body 10)
    (lst (+ body 1)))
  (list body lst))

编辑:问题得到回答,通过编辑Lief Andersen代码添加了解决方案而不使用 let

(define-syntax let*
  (syntax-rules ()
    ((let*2 ([x1 e1][x2 e2] ...)body ...)
     ((lambda  (x1)
        (let* ([x2 e2] ...)
           body ...))
         e1))))
4

3 回答 3

1

R6RS,附录 B 给出了以下定义let*

(define-syntax let*
  (syntax-rules ()
    ((let* () body1 body2 ...)
     (let () body1 body2 ...))
    ((let* ((name1 expr1) (name2 expr2) ...)
       body1 body2 ...)
     (let ((name1 expr1))
       (let* ((name2 expr2) ...)
         body1 body2 ...)))))
于 2018-04-11T18:09:55.827 回答
0

你的第二个是最接近的。卫生let*宏可以用define-syntax-rule. (如果您是在实现方案而不是球拍中编写此代码,您也可以只编写define-syntaxsyntax-rule获得相同的效果。)

(define-syntax-rule (let* ([x1 e1]
                           [x2 e2] ...)
                      body ...)
  (let ([x1 e1])
    (let* ([x2 e2] ...)
      body ...))

如果你觉得很迂腐,你可以做一个卫生的let宏:

(define-syntax-rule (let ([x e] ...)
                      body ...)
  ((lambda (x ...) body ...) e ...))
于 2018-04-11T18:00:17.537 回答
0

说到卫生,let*我认为卫生不是问题。您要公开的名称let并且不引入任何其他绑定。

(require compatibility/defmacro)
(define-macro let*1
  (lambda (assgn . body)
    (let loop ((lst assgn))
      (if (null? lst)
          `(begin ,@body)
          `((lambda (,(caar lst))
              ,(loop (cdr lst)))
            ,(cadar lst))))))

(expand-once 
 #'(let*1 
     ((a 1) (b 2) (c 3)) 
       (list a b c)))

; ==> #'((lambda (a)
;          ((lambda (b)
;            ((lambda (c)
;               (begin
;                 (list a b c)))
;             3))
;          2))
;       1)

对我来说似乎没问题。现在您可以使用syntax-rules. 此方法不使用方案语言,因此您尝试做(caar lst)的是假设您希望在结果扩展中使用它

(expand-once #'(let*2 ((a 1) (b 2) (c 3)) (list a b c)))
; ==> #'(let loop ((lst '((a 1) (b 2) (c 3))))
;         (if (null? lst)
;             (list a b c)
;             ((lambda ((caar lst))
;                (loop (cdr lst)))
;              (cadar lst)
;              1)))

请注意您的代码实际上是如何逐字复制到生成的扩展中的。这是因为模式中未解构的所有内容都被假定在语法本身的词法范围内。这是如何做到的:

(define-syntax let*2
  (syntax-rules ()
    ;; handle stop condition (no more bindings)
    ((let*2 ()  body ...) 
     (begin body ...))
    ;; handle one expansion
    ((let*2 ((name expr) more-bindings ...) body ...)
     (let ((name expr))
       (let*2 (more-bindings ...)
         body ...))))) 
(expand-once 
 #'(let*2 
     ((a 1) (b 2) (c 3)) 
       (list a b c)))

; ==> #'((lambda (a) 
;          (let*2 ((b 2) (c 3)) 
;            (list a b c))) 1)

expand-once只做一个级别,但是通过使用宏扩展器,您会看到它一直持续到所有部分都被扩展为止。

那么什么时候需要卫生呢?当您在宏中引入绑定时。标准示例通常swap!是交换两个绑定的值:

(define-syntax swap!
  (syntax-rules ()
    ((swap! var1 var2)
     (let ((tmp var1))
       (set! var1 var2)
       (set! var2 tmp)))))

对于syntax-rules每一个绑定,一个模式都不是在宏的词法范围内,而且一切都不是一个新的绑定,在每次扩展时都会创建新的绑定。因此我可以安全地做到这一点:

(let ((tmp 1) (another 2))
  (swap! tmp another)
  (list tmp another))
; ==> (2 1)

使用旧样式,您需要:

(define-macro swap!
  (lambda (var1 var2)
    (let ((tmp (gensym "tmp")))
      `(let ((,tmp ,var1))
         (set! ,var1 ,var2)
         (set! ,var2 ,tmp)))))

这适用于上面的示例,但如果我这样做:

(let ((set! 1) (let 2))
  (swap! set! let)
  (list set! let))

一个将评估的语法规则,(2 1)define-macro一个不起作用。

于 2018-04-11T18:31:52.357 回答