球拍宏自动定义函数给出一个列表(Racket Macro to auto-define functi

2019-09-03 11:42发布

我想自动生成从列表一堆的测试功能。 其优点是我可以改变列表(通过在CSV数据表中读取EG),程序将自动生成的下一个程序执行不同的测试。

例如,假设我试图找出氧阴离子中含有的字符串化学式 。

我的列表可能是这样的:

(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)”),或者如果所述阳离子接着用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))))])))

感谢您能不能给我任何指导!


PS这里是要经过一些测试:

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

Answer 1:

我看到一对夫妇在代码中的错误:

  1. 你*氧离子的测试*是一个运行时的值,但你需要它的值作为函数名称标识符来使用,所以它必须在编译时可用。
  2. syntax周围的结果syntax-rules是隐含的。 因此,与syntax-rules ,你只能得到宏模板语言(请参阅文档syntax获取更多信息)。 因此,你不能做datum->syntax ,你正在尝试做的。 您必须使用syntax-case ,它可让您使用所有的球拍来计算你想要的语法对象。

以下是我想出了:

#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->syntaxstx作为上下文,这意味着定义的函数将在宏被调用的上下文是可用的。



文章来源: Racket Macro to auto-define functions given a list
标签: macros racket