4

Common Lisp を独学で学ぼうとしており、マクロ作成の演習として、任意の深さのネストされた do ループを定義するマクロを作成しようとしています。私はemacsとスライムを使ってsbclを使っています。

まず、次の二重ループ マクロを作成しました。

(defmacro nested-do-2 (ii jj start end &body body)
  `(do ((,ii ,start (1+ ,ii)))
       ((> ,ii ,end))
     (do ((,jj ,ii (1+ ,jj)))
         ((> ,jj ,end))
       ,@body)))

次に、次のように使用できます。

(nested-do-2 ii jj 10 20 (print (+ ii jj)))

ところで、もともとこのマクロは gensym を使用してループ カウンター (ii、jj) を生成するように記述していましたが、ボディ内のカウンターにアクセスできなければ、このマクロはほとんど役に立たないことに気付きました。

とにかく、マクロを一般化して、任意のレベルにネストされるネストされた do ループを作成したいと思います。これは私がこれまでに得たものですが、うまくいきません:

(defmacro nested-do ((&rest indices) start end &body body)
  `(dolist ((index ,indices))
     (do ((index ,start (1+ index)))
          ((> index ,end))
        (if (eql index (elt ,indices (elt (reverse ,indices) 0)))
            ,@body))))

私は次のように呼び出したいと思います:

(nested-do (ii jj kk) 10 15 (print (+ ii jj kk)))

ただし、リストが適切に展開されていないため、デバッガーで次のエラーが発生します。

error while parsing arguments to DEFMACRO DOLIST:                                              
  invalid number of elements in                                                                
    ((INDEX (II JJ KK)))

そして、それが明らかでない場合、埋め込まれた if ステートメントのポイントは、最も内側のループでのみ本体を実行することです。それは私にはあまりエレガントに見えず、実際にはテストされていません (パラメーターリストをまだ展開できていないため) が、実際にはこの質問のポイントではありません。

マクロ内でリストを適切に展開するにはどうすればよいですか? マクロの構文に問題がありますか、それとも関数呼び出しのリストの式に問題がありますか? その他のコメントもお待ちしております。

前もって感謝します。

4

2 に答える 2

0

[反対票は別として、これは実際に機能し、美しいです!]

マクロ定義の再帰的な性質を明確に説明するために、Scheme の実装を次に示します。

(define-syntax nested-do
  (syntax-rules ()
    ((_ ((index start end)) body)
     (do ((index start (+ 1 index)))
         ((= index end))
       body))

    ((_ ((index start end) rest ...) body)
     (do ((index start (+ 1 index)))
         ((= index end))
       (nested-do (rest ...) body)))))

上記をテンプレートとして使用すると、次のようになります。

(defmacro nested-do ((&rest indices) start end &body body)
  (let ((index (car indices)))
    `(do ((,index ,start (1+ ,index)))
         ((> ,index ,end))
       ,(if (null (cdr indices))
            `(progn ,@body)
            `(nested-do (,@(cdr indices)) ,start ,end ,@body)))))


* (nested-do (i j) 0 2 (print (list i j)))
(0 0) 
(0 1) 
(0 2) 
(1 0) 
(1 1) 
(1 2) 
(2 0) 
(2 1) 
(2 2) 
NIL

すべての Common-Lisp マクロでは、変数のキャプチャを避けるために「gensym」パターンを使用する必要があることに注意してください。

于 2013-03-14T15:38:20.573 に答える