2

リストから多数のテスト関数を自動生成したいと考えています。利点は、リストを変更できること (たとえば、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 )" )、または陽イオンの後に 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")))
4

1 に答える 1

2

コードにいくつかのエラーが表示されます。

  1. *oxyanion-tests* は実行時の値ですが、その値を関数名識別子として使用する必要があるため、コンパイル時に使用できる必要があります。
  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->syntaxwithをコンテキストとして使用しstxます。つまり、マクロが呼び出されるコンテキストで、定義された関数を使用できます。

于 2013-05-16T06:27:42.163 に答える