Macros Racket宏自动定义给定列表的函数

Macros Racket宏自动定义给定列表的函数,macros,racket,Macros,Racket,我想从列表中自动生成一组测试函数。优点是我可以更改列表(例如,通过读取CSV数据表),程序将在下一次程序执行时自动生成不同的测试 例如,假设我试图在一个包含 我的清单可能是这样的: (define *oxyanion-tests* ; name cation (list (list "aluminate" "Al") (list "borate" "B") (list "gallate" "Ga")

我想从列表中自动生成一组测试函数。优点是我可以更改列表(例如,通过读取CSV数据表),程序将在下一次程序执行时自动生成不同的测试

例如,假设我试图在一个包含

我的清单可能是这样的:

(define *oxyanion-tests*
  ;           name         cation
  (list (list "aluminate"  "Al")
        (list "borate"     "B")
        (list "gallate"    "Ga")
        (list "germanate"  "Ge")
        (list "phosphate"  "P")
        (list "sulfate"    "S")
        (list "silicate"   "Si")
        (list "titanate"   "Ti")
        (list "vanadate"   "V")
        (list "stannate"   "Sn")
        (list "carbonate"  "C")
        (list "molybdate"  "Mo")
        (list "tungstate"  "W")))
我有理由相信,如果括号内有一个阳离子后跟一个氧(例如“(C O3)”),或者如果阳离子后跟两个或更多的氧(例如“C O3”),则化学式包含其中之一。请注意,这并不是完美的,因为它会漏掉次氯酸盐阴离子(例如“Cl O”),但对于我的应用来说已经足够好了

(define ((*ate? elem) s-formula)
  (or (regexp-match? (regexp (string-append "\\(" elem "[0-9.]* O[0-9.]*\\)")) s-formula)
      (regexp-match? (regexp (string-append "(^| )" elem "[0-9.]* O[2-9][0-9.]*")) s-formula)))
我想我需要一个宏来实现这一点,但我并不真正理解它们是如何通过阅读文档来工作的。我在这里问这个问题,是为了让我有一个很好的例子来看看,这对我很有帮助

这是我认为宏应该是什么样子的,但它不起作用,我也没有一个大脑模型来找出如何修复它

(require (for-syntax racket))
(define-syntax-rule (define-all/ate? oxyanion-tests)
  (for ([test oxyanion-tests])
    (match test
      [(list name cation) (syntax->datum (syntax (define ((string->symbol (string-append name "?")) s-formula)
                                    ((*ate? cation) s-formula))))])))
谢谢你能给我的任何指导


这里有一些应该通过的测试:

(define-all/ate? *oxyanion-tests*)
(module+ test
  (require rackunit)
  (check-true (borate? "B O3"))
  (check-true (carbonate? "C O3"))
  (check-true (silicate? "Si O4")))

我看到您的代码中有几个错误:

  • 您的*OxyAnyon tests*是一个运行时值,但您需要将其值用作函数名标识符,因此它必须在编译时可用
  • 的结果周围的
    语法是隐式的。因此,使用
    语法规则
    ,您只能获得宏模板语言(有关更多信息,请参阅文档)。因此,您无法执行您试图执行的
    datum->syntax
    。您必须使用,这允许您使用所有Racket来计算所需的语法对象
  • 以下是我的想法:

    #lang racket
    (require (for-syntax racket/syntax)) ; for format-id
    
    (define-for-syntax *oxyanion-tests*
      ;           name         cation
      (list (list "aluminate"  "Al")
            (list "borate"     "B")
            (list "gallate"    "Ga")
            (list "germanate"  "Ge")
            (list "phosphate"  "P")
            (list "sulfate"    "S")
            (list "silicate"   "Si")
            (list "titanate"   "Ti")
            (list "vanadate"   "V")
            (list "stannate"   "Sn")
            (list "carbonate"  "C")
            (list "molybdate"  "Mo")
            (list "tungstate"  "W")))
    
    (define ((*ate? elem) s-formula)
      (or (regexp-match? 
           (regexp (string-append "\\(" elem "[0-9.]* O[0-9.]*\\)")) 
           s-formula)
          (regexp-match?
           (regexp (string-append "(^| )" elem "[0-9.]* O[2-9][0-9.]*")) 
           s-formula)))
    
    (define-syntax (define-all/ate? stx)
      (syntax-case stx ()
        [(_)
         (let ([elem->fn-id 
                (λ (elem-str)
                  (format-id 
                   stx "~a?" 
                   (datum->syntax stx (string->symbol elem-str))))])
           (with-syntax 
             ([((ate? cation) ...)
               (map 
                (λ (elem+cation)
                  (define elem (car elem+cation))
                  (define cation (cadr elem+cation))
                  (list (elem->fn-id elem) cation))
                *oxyanion-tests*)])
             #`(begin
                 (define (ate? sform) ((*ate? cation) sform))
                 ...)))]))
    
    (define-all/ate?)
    (module+ test
      (require rackunit)
      (check-true (borate? "B O3"))
      (check-true (carbonate? "C O3"))
      (check-true (silicate? "Si O4")))
    
    关键是
    elem->fn id
    函数,它将字符串转换为函数标识符。它使用
    datum->syntax
    stx
    作为上下文,这意味着定义的函数将在调用宏的上下文中可用