2

背景:

開発中のコードのデバッグ メッセージを生成するつもりです。各関数でロギング呼び出しを記述しないようにマクロを作成しました。これにより、よりカスタマイズされたデバッグ メッセージを生成する機能が制限されることはわかっていますが、代わりにログをコードから分離します。そして、それが私が目指しているものです。このマクロ アプローチには他にも欠点があります。たとえば、関数バインディングの作成がこのマクロのみに制限されますが、私はそれを受け入れることができると思います。

以下は、マクロの定義とその使用法を示す例です。

(define-syntax (define-func stx)
  (syntax-case stx ()
    [(define-func (func-name args ...) body1 body2 ...)
     (if (and (identifier? #'func-name)
              (andmap symbol? (syntax->datum #'(args ...))))
       (syntax (define (func-name args ...)
                 (log-debug (format "Function-name ~a:" (syntax-e #'func-name)) (list args ...))
                 body1
                 body2 ...))
       (raise-syntax-error 'define-func "not an identifier" stx))]
    [else (raise-syntax-error 'define-func "bad syntax" stx)]))



(define-func (last l)
  (cond [(null? l) null]
        [(null? (rest l)) (first l)]
        [else (last (rest l))]))


(define-func (main)
  (last (list 1 2 3 4 5 6 7 8 9))
  (logger))

log-debug と logger は別のモジュールで定義されています

生成される出力は、次のようになります。

Function-name last: 
args: 
:-> (7 8 9) 


Function-name last: 
args: 
:-> (8 9) 


Function-name last: 
args: 
:-> (9) 

これからはもっと読みやすくしたいです。読みやすさとは、ログを読んでいる人が通話の流れを理解できるように、ある種のインデントを提供することを意味します。たとえば、次のようなものです。

Function-name last: 
args: 
:-> (7 8 9) 

    Function-name last: 
    args: 
    :-> (8 9) 

        Function-name last: 
        args: 
        :-> (9) 

誰が誰に電話したかなどを簡単に把握できます。これができるアイデアがあります。インデントを追跡する変数が含まれており、関数名をログに記録した後、インデントを増やし、本体の評価後、値を返す前に値を減らします。次のようなもの:

(define indent 0)

(define-syntax (define-func stx)
  (syntax-case stx ()
    [ (... ...)
      (...
      (log-debug ...)
      (increment indent)
      (let [(retval (body1 body2 ...)]
        (decrease indent)
        retval))]))

インクリメントとデクリメントは、それぞれインデントを増減します。

問題:

void を返す関数に対しても機能します。それが正しい動作かどうかはわかりません。ラケットでは void は特別な値ですが、void へのバインディングを作成することが正しい方法かどうかはわかりません。

同じことを達成するためのより良い方法はありますか? そうでない場合、この設計に問題はありますか? ロギングとコードを別々に保つ限り、私はどんなアイデア/変更にもオープンです。

助けてくれてありがとう!

4

1 に答える 1

4

いくつか提案があります。

  • 元の値が式の最後に復元されるため、インデント レベルなどの「グローバル」なものについては、変数の代わりにパラメーターを使用する方がおそらく良いでしょうparameterize
  • マクロにあるこれらのraise-syntax-errorチェックはすべて完全にsyntax-case不要です。必要なマクロの「引数」の検証を行うことができるガード (フェンダーとも呼ばれます) が既に提供されています。

    (define-syntax (define-func stx)
      (syntax-case stx ()
        [(_ (func-name args ...) body1 body2 ...)
         (andmap identifier? (syntax->list #'(func-name args ...)))
         #'(define (func-name args ...)
             (log-debug (format "Function-name ~a:" 'func-name)
                        (list args ...))
             body1
             body2 ...)]))
    

    上記のように、いくつかの場所でコードを修正しました。

    1. (_ ...)の代わりに使用したのは(define-func ...)syntax-case( とは異なりsyntax-rules) では、後者は実際に というパターン変数をバインドするためdefine-funcです。とにかく良い習慣)。
    2. ガード内の を完全に平坦化するのではなく、#'(args ...)を使用してテストできるように構文オブジェクトのリストに変えただけですidentifier?。これは、 を使用してテストするよりも意図を明らかにし、同じ式でsymbol?テストすることもできます。func-name
    3. (syntax-e #'func-name)展開されたコード内で使用する必要はありません! 引用するだけです。
于 2013-09-13T16:47:52.747 に答える