2013-05-15 36 views
2

我想從列表中自動生成一堆測試函數。好處是我可以更改列表(例如通過讀取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個或更多個氧原子(例如」C03「)。請注意,這並不完美,因爲它會漏掉次氯酸鹽陰離子(例如「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"))) 

回答

2

我看到一對夫婦的錯誤在你的代碼:

  1. 你*氧離子的測試*是一個運行時的值,但你需要它的值用作爲函數名稱標識符,所以它必須在編譯時可用。
  2. syntax圍繞syntax-rules結果是隱含的。因此,對於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->syntaxstx作爲上下文,這意味着定義的函數將在宏調用的上下文中可用。