2016-06-25 5 views
2

変数のリストとコード本体を取り、コードの本体が実行された後に変数が元の値に戻ることを確認するマクロを作成しようとしています(Paul Graham's ANSI Common Lisp)。gensymシンボルのリスト内でマクロが評価されない

はしかし、私は私のgensymは、私は一つの場所でそれを期待してではなく、他の同様のもの(ノートとして評価される理由については不明だ:私は運動へのよりよい解決策があると知っている私はちょうど把握したいですなぜ評価の違いか)。

ここlst gensymははmapcarに渡されたlambdaの内部のリストに評価された最初の定義です:

(defmacro exec-reset-vars-1 (vars body) 
    (let ((lst (gensym))) 
    `(let ((,lst ,(reduce #'(lambda (acc var) `(cons ,(symbol-value var) ,acc)) 
          vars 
          :initial-value nil))) 
     ,@body 
     ,@(mapcar #'(lambda (var) `(setf ,var (car ,lst))) 
        vars)))) 

しかし、私はそれが期待通りに正確に動作しますが、それが正しい解決策ではありませんなぜなら、値をリセットしようとすると、私はいつもlstの最初の要素をつかんでいるからです。私は本当に2つのリストをマップしたい。だから今私は書く:

(defmacro exec-reset-vars-2 (vars body) 
    (let ((lst (gensym))) 
    `(let ((,lst ,(reduce #'(lambda (acc var) `(cons ,(symbol-value var) ,acc)) 
          vars 
          :initial-value nil))) 
     ,@body 
     ,@(mapcar #'(lambda (var val) `(setf ,var ,val)) 
        vars 
        lst)))) 

しかし、今私は#:G3984がリストではないというエラーが表示されます。私が(symbol-value lst)に置き換えた場合、変数に値がないというエラーが表示されます。しかし、どうしてですか?なぜの内部の値がlambdaにありますが、引数がmapcarに渡されないのはなぜですか?

答えて

6

マクロ展開時に、そのときのシンボルであるlstの値をマップしようとします。それは意味をなさない。

lstのバインディングは字句であり、symbol-valueはそれにアクセスする手段ではないため、シンボル値を取得しようとすると意味がありません。他のバインディングはその時点では利用できません。

明らかにlstには、マクロ展開時間に値があります。記号。これはあなたがラムダ内で見るものです。

マクロ展開時に計算される値と実行時に計算される値を明確にする必要があります。

命名についてのアドバイス:

  • lstは、その値がリストではなく、シンボル以来、list
  • lstは意味をなさない使用Lispでは貧しい名前です。私はそれをlist-variable-symbolと呼ぶだろう。長く見えますか?しかし、はるかに明確です。これはシンボルであり、変数リストを保持する変数の名前として使用されます。
+0

ありがとうございます。私が正しく理解するために、どちらの場合も 'lst'はマクロ展開時に同じ値を持っています。違いは、最初のケースで '(car、lst)'が展開され、展開されたフォームがランタイム中に評価され、 'lst'がリストである点です。一方、第2引数では 'mapcar'への引数としてマクロ展開中に評価されています。これは正しいです? –

+1

@UnixOne:いいえ。実行時には 'lst'はありませんが、マクロ展開時には' lst'の値だった名前の変数があります。 –

3

私はあなたがそれを思い知らされていると確信しています。これを想像:

(defparameter *global* 5) 
(let ((local 10)) 
    (with-reset-vars (local *global*) 
    (setf *global* 20) 
    (setf local 30) 
    ...)) 

私は拡張がするのと同じくらい簡単です想像:あなたはマクロだけでletと影のバインディングを作る非常に単純であることを見て

(defparameter *global* 5) 
(let ((local 10)) 
    (let ((*global* *global*) (local local)) 
    (setf *global* 20) 
    (setf local 30) 
    ...) 
    (print local)) ; prints 10 
(print *global*) ; prints 5 

letは、それ自身のリセットを行います私がその課題を誤解していない限り。

あなたの非常に複雑なマクロはかなり悪いことをします。グローバルシンボルの値を取得するのと同じように、コンパイル時に、ボディの前ではなく、これを使った関数の時間にリセットされます。

+0

ありがとうございます。あなたは、絶対に正しい。私が指摘したように、私はそれが運動の良い解決策ではないことを知っています、私はちょうどgensymがなぜそれを1つの場所で評価したのだろうと思っていました。 –

+1

@UnixOne Thats easy。最初は、実行時にバインドされる 'let'バインディングの最初の部分を与える'(car lst-gensym-symbol) 'を行いますが、2番目ではマクロ展開時には同じにマップしようとしますが、フェーズはリストではなくシンボル値です。マクロは基本的にコード変換であり、可変バインディングに頼るべきではありません。 – Sylwester

関連する問題