我想重构这种模式,其中许多顶级变量都使用给定的命名方案分配。
#lang racket
(define ((f-x/y x y) z)
;; nominal example
(list x y z))
(match-define (list f-a/a f-a/b f-a/c
f-b/a f-b/b f-b/c
f-c/a f-c/b f-c/c)
(for*/list ([x '(a b c)]
[y '(a b c)])
(f-x/y x y)))
(f-b/a 'd) ;; => '(b a d)
问题似乎有两个方面:在循环内定义任何内容,以便可以在外部看到它(违反词法范围?),并定义一个名称由模板创建的变量。
理想情况下,这样的事情是可能的;
format-id
至少看起来是正确的方向。
(require racket/syntax)
;; notional
(for*/defs ([x '(a b c)]
[y '(a b c)])
(define (format-id #'x "f-~a/~a" x y)
(f-x/y x y)))
就其价值而言,所有
'(a b c)
元素都是固定的并且在编译时是已知的。上面的第一个代码块可以工作并且在实践中很好,但我真的很想学习可能存在的解决此类问题的宏观技术。
我以前做过类似的事情,是的,
format-id
是正确的方法。词法上下文可以只是传递给使用该函数的宏的顶级语法对象。
一个例子:
#lang racket/base
(require (for-syntax racket/base racket/list racket/syntax syntax/parse))
(define ((f-x/y x y) z) (list x y z))
(define-syntax (define-all-options stx)
(syntax-parse stx ; Use syntax-parse instead of syntax-case to get type checks in the macro pattern
[(_ f:id (sym:id ...))
(let* ([symbols (syntax->list #'(sym ...))] ; list of syntax objects for the given symbol
[pairs (cartesian-product symbols symbols)] ; every combination of 2 ids
[identifiers ; build the names of the functions to define
(for/list ([combo (in-list pairs)])
(format-id stx "f-~a/~a" (first combo) (second combo)))]
[constructors ; build the function calls whose return values are bound to the above names
(for/list ([combo (in-list pairs)])
#`(f #,@combo))])
;; and put all the parts together
#`(define-values #,identifiers (values #,@constructors)))]))
(define a 'a)
(define b 'b)
(define c 'c)
(define-all-options f-x/y (a b c))
(println (f-a/b 'd))
我发现最好只采用文字标识符而不是引用列表,以保持宏中的简单。这确实意味着需要在调用宏之前定义这些标识符。