问题描述
我想从列表中自动生成一堆测试函数.优点是我可以更改列表(例如,通过读取 CSV 数据表)并且程序将在下一次程序执行时自动生成不同的测试.
I want to auto-generate a bunch of test functions from a list. The advantage being I can change the list (e.g. by reading in a CSV data table) and the program will auto-generate different tests on the next program execution.
例如,假设我试图在包含 oxyanions="http://en.wikipedia.org/wiki/Chemical_formula" rel="nofollow">化学式.
For example, say I am trying to identify oxyanions in a string containing a chemical formula.
我的列表可能是这样的:
My list may be something like:
(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")))
如果有阳离子后跟括号内的氧(例如(CO3)"),或者如果阳离子后跟2个或更多个氧(例如CO3").请注意,这并不完美,因为它会遗漏次氯酸盐阴离子(例如Cl O"),但对于我的应用来说已经足够了.
I'm reasonably confident that the chemical formula contains one of these oxyanions if there is a cation followed by an oxygen within parentheses (e.g. "(C O3)" ), or if the cation is followed by 2 or more oxygens (e.g. "C O3"). Note that this isn't perfect, since it will miss hypochlorite anions (e.g. "Cl O"), but it's good enough for my application.
(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)))
我认为我需要一个宏来执行此操作,但通过阅读文档我并不真正了解它们是如何工作的.我在这里问是为了让我有一个很好的例子来看看这对我很有用.
I think I need a macro to do this, but I don't really understand how they work from reading the documentation. I'm asking here so that I have a good example to look at that is immediately useful to me.
我认为宏应该是这样的,但它不起作用,我真的没有一个心智模型来弄清楚如何修复它.
Here is what I kind of think the macro should look like, but it doesn't work and I don't really have a mental model for figuring out how to fix it.
(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))))])))
感谢您能给我的任何指导!
Thanks for any guidance you can give me!
附:以下是一些应该通过的测试:
P.S. Here are a few tests that should pass:
(define-all/ate? *oxyanion-tests*)
(module+ test
(require rackunit)
(check-true (borate? "B O3"))
(check-true (carbonate? "C O3"))
(check-true (silicate? "Si O4")))
推荐答案
我发现您的代码中有几个错误:
I see a couple of errors in your code:
- 您的 *oxyanion-tests* 是一个运行时值,但您需要将其值用作函数名称标识符,因此它必须在编译时可用.
- 是隐式的.因此,使用
syntax-rules
,您只能获得宏模板语言(请参阅syntax
了解更多信息).因此,您无法执行您正在尝试执行的datum->syntax
.您必须使用syntax-case
相反,它允许您使用所有 Racket 来计算您想要的语法对象.
- Your *oxyanion-tests* is a runtime value, but you need its values to use as function name identifiers, so it must be available at compile time.
- The
syntax
around the result ofsyntax-rules
is implicit. So withsyntax-rules
, you only get the macro template language (see the docs forsyntax
for more info). Thus you can't do thedatum->syntax
that you are trying to do. You have to usesyntax-case
instead, which allows you to use all of Racket to compute the syntax objects you want.
这是我想出的:
#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
作为上下文,这意味着定义的函数将在调用宏的上下文中可用.
The key is the elem->fn-id
function, which turns a string into a function identifier. It uses datum->syntax
with stx
as the context, meaning the defined function will be available in the context where the macro is invoked.
这篇关于给定列表自动定义功能的球拍宏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!