2012年3月29日木曜日

defsetfの中のgensymについて

インヴァージョンを定義するためのdefsetfでハマったのでメモ
今回やりたかったことは,(setf (mref array index1 index2) new-element)とし,accessという名前のマクロで参照されるarrayの要素にnew-elementを束縛すること.
common lispには既にarefという関数があるが,ある行の全ての要素へのアクセスや,行列の部分行列を抜き出しを便利にするためのマクロであるmrefを新たに定義し,それに対するインヴァージョンを定義する.

ここで,mrefは,(mref array rs_re cs_ce)とすれば,arrayのrs+1行目からre行目,cs+1列目からce列目の部分行列を返すもので,(mref array 1 _)とかで1行目の横ベクトルをとってくることもできるように作ってある.

ここで問題になったのはdefsetfの変数補足の問題を減らすために導入されているgensym.
今回の問題の単純な例を示すと,

access-fnにあたるマクロ:
(defmacro aho (x) `(list ',x))
update-fnにあたるマクロ:
(defmacro baka (x y) `(list ',x ',y))

まずは,defsetfの使い方1(短いフォーム)でインヴァージョンを定義すると,
(defsetf aho baka)
で,例えば(setf (aho _) 3)を評価すると(_ 3)となる.これは想定通りの動き.

ところが,defsetfの使い方2(長いフォーム)でインヴァージョンを定義すると,
(defsetf aho (x) (y) `(list ',x ',y))
で,同様に(setf (aho _) 3)を評価すると,「The variable _ is unbound.」というエラーが出る.
defsetfはマクロの定義と同様だという頭でいると,xに与えられた _ というシンボルが評価される訳が無く,`(list ',x ',y)は,(list '_ '3)の様に展開されて,(_ 3)が返ってくるはず!と思いこんでしまう.
しかし,defsetfの中でgensymが使われているという事を思い出して考えてみると,
恐らく,
   (defmacro ..................... (x y)
       (let ((tmp1 (gensym))
              (tmp2 (gensym)))
           `(let* ((,tmp1 ,x)
                     (,tmp2 ,y))
                   ....................
みたいな事になっているので,評価されないと思い込んでたシンボル _ が評価されてしまうことになる.
いやはや,奥が深いねぇ~

0 件のコメント: