在球拍/方案中展开'for'循环的宏?

gab*_*lin 5 macros scheme for-loop racket

我正在尝试在racket/scheme中编写一个宏,它for在一些任意代码中循环操作,以便循环体展开.例如,以下代码

(macro-for ((i '(0 1 2 3))
  (another-macro
    (with i)
    (some (nested i))
    (arguments (in (it (a b c i))))))
Run Code Online (Sandbox Code Playgroud)

应该具有与代码编写为相同的结果

(another-macro
  (with 0)
  (some (nested 0))
  (arguments (in (it (a b c 0))))))

(another-macro
  (with 1)
  (some (nested 1))
  (arguments (in (it (a b c 1))))))

(another-macro
  (with 2)
  (some (nested 2))
  (arguments (in (it (a b c 2))))))
Run Code Online (Sandbox Code Playgroud)

我试图实现它,但我是宏的新手,它们似乎不像我期望的那样工作.这是我的尝试 - 它不能编译,因为match显然不允许在宏中使用 - 但希望它传达了我想要实现的想法.

(module test racket

(require (for-syntax syntax/parse))

(begin-for-syntax
  (define (my-for-replace search replace elem)
    (if (list? elem)
        (map (lambda (e) (my-for-replace search replace e)) elem)
        (if (equal? elem search)
            replace
            elem))))

(define-syntax (my-for stx)
  (syntax-case stx ()
    ((my-for args-stx body-stx)
     (let ((args (syntax-e #'args-stx)))
       (if (list? args)
           (map (lambda (arg)
                  (match arg
                         ((list #'var #'expr)
                          (my-for-replace #'var #'expr #'body))
                         (else
                          (raise-syntax-error #f
                                              "my-for: bad variable clause"
                                              stx
                                              #'args))))
                args)
           (raise-syntax-error #f
                               "my-for: bad sequence binding clause"
                               stx
                               #'args))))))

(define-syntax (my-func stx)
  (syntax-parse stx
                ((my-func body)
                 #'body)))

(my-for ((i '(0 1 2)))
        (my-func (begin
                   (display i)
                   (newline))))


)
Run Code Online (Sandbox Code Playgroud)

Rya*_*per 6

这是我写的方式(如果我要编写类似的东西):

首先,我们需要一个辅助函数,它可以在一个语法对象中替换标识符出现在另一个语法对象中的任何位置.注意:永远不要使用syntax->datum您打算将其视为表达式(或包含表达式或定义等)的内容.相反,使用syntax-e和处理后递归展开将其重新组合在一起,就像之前一样:

(require (for-syntax racket/base))
(begin-for-syntax
  ;; syntax-substitute : Syntax Identifier Syntax -> Syntax
  ;; Replace id with replacement everywhere in stx.
  (define (syntax-substitute stx id replacement)
    (let loop ([stx stx])
      (cond [(and (identifier? stx) (bound-identifier=? stx id))
             replacement]
            [(syntax? stx)
             (datum->syntax stx (loop (syntax-e stx)) stx stx)]
            ;; Unwrapped data cases:
            [(pair? stx)
             (cons (loop (car stx)) (loop (cdr stx)))]
            ;; FIXME: also traverse vectors, etc?
            [else stx]))))
Run Code Online (Sandbox Code Playgroud)

使用bound-identifier=?,当你实现一个结合样的关系,如替代.(这是一种罕见的情况;通常free-identifier=?是正确的比较使用.)

现在宏只是解释for子句,替换,并组装结果.如果您确实希望将术语列表替换为编译时表达式,请使用syntax-local-evalfrom racket/syntax.

(require (for-syntax racket/syntax))
(define-syntax (macro-for stx)
  (syntax-case stx ()
    [(_ ([i ct-sequence]) body)
     (with-syntax ([(replaced-body ...)
                    (for/list ([replacement (syntax-local-eval #'ct-sequence)])
                      (syntax-substitute #'body #'i replacement))])
       #'(begin replaced-body ...))]))
Run Code Online (Sandbox Code Playgroud)

这是一个使用示例:

> (macro-for ([i '(1 2 3)]) (printf "The value of ~s is now ~s.\n" 'i i))
The value of 1 is now 1.
The value of 2 is now 2.
The value of 3 is now 3.
Run Code Online (Sandbox Code Playgroud)

请注意,它替换了i引号下的出现,因此您永远不会i在输出中看到该符号.那是你的期望吗?


免责声明:这不代表典型的Racket宏.在未展开的形式中搜索和替换通常是一个坏主意,并且通常有更多惯用的方法来实现您想要的.