我想从列表中自动生成一堆测试函数。我可以更改列表的优点(例如,通过读取CSV数据表),程序将在下一个程序执行时自动生成不同的测试。
例如,假设我尝试在包含oxyanions的字符串中标识chemical formula。
我的列表可能是这样的:
(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")))
我有理由相信化学式中含有oxyanions中的一种,如果有一个阳离子,后面是括号内的氧(例如“(C O3)”),或者阳离子后面是2或更多的氧(例如“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))))])))
感谢你给我的任何指导!
P.S。以下是一些应该通过的测试:
(define-all/ate? *oxyanion-tests*)
(module+ test
(require rackunit)
(check-true (borate? "B O3"))
(check-true (carbonate? "C O3"))
(check-true (silicate? "Si O4")))
答案 0 :(得分:2)
我在您的代码中看到了一些错误:
syntax-rules
结果周围的syntax
是隐式的。因此,对于syntax-rules
,您只能获得宏模板语言(有关详细信息,请参阅syntax
的文档)。因此,您无法执行您尝试执行的datum->syntax
。您必须使用syntax-case
,这样您就可以使用所有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
作为上下文,这意味着定义的函数将在调用宏的上下文中可用。