Previous: , Up: 汎変数   [Contents][Index]


4.2.3 setf独自化

Common Lispは、さまざまな方法で汎変数を拡張することをユーザに許す3個の マクロ、define-modify-macrodefsetf、そして define-setf-methodを定義する。

Special Form: define-modify-macro name arglist function [doc-string]

このマクロはincfdecfに類似する“read-modify-write” マクロを定義する。マクロnamearglistによって記述される 追加の引数が続くplace引数をとるために定義される。呼び出し

(name place args...)

は下記に展開されるが

(callf func place args...)

これは順に大まかに以下と同等である。

(setf place (func place args...))

たとえば:

(define-modify-macro incf (&optional (n 1)) +)
(define-modify-macro concatf (&rest args) concat)

&keyarglistの中には許されないが、&restは関数へ キーワードを渡すためには十分なことに注意せよ。

Common Lispで定義される修正マクロのほとんどは define-modify-macroのパターンに正確には従っていない。たとえば、 pushはよくない順序で引数をとり、popは完全に変則である。 get-setf-methodを使って“手で”これらのマクロを定義できるし、 内部のsetfビルディングブロックをどのように使うかを見るために ソースファイルcl-macs.elを調べることもできる。

Special Form: defsetf access-fn update-fn

これは2個のdefsetfフォームのより単純な方である。access-fn が場所をアクセスする関数の名前である場合、これは対応する格納関数として update-fnを宣言する。その後、

(setf (access-fn arg1 arg2 arg3) value)

は以下に展開される。

(update-fn arg1 arg2 arg3 value)

update-fnは真の関数か、関数のような方法でその引数を評価する マクロであることが必須である。また、update-fnはその結果として valueを戻すことを期待される。さもなければ、上の展開はsetf が振る舞うことになっていることのための規則に従わないだろう。

特殊な(非Common Lisp)拡張として、defsetfへのtの第3引数は update-fnの戻り値は適切でないと言っているので、上のsetf はより以下に似たようなものへ展開されるべきである。

(let ((temp value))
  (update-fn arg1 arg2 arg3 temp)
  temp)

setfメソッドの標準一式から引かれた、defsetfの使用のいくつかの 例は:

(defsetf car setcar)
(defsetf symbol-value set)
(defsetf buffer-name rename-buffer t)
Special Form: defsetf access-fn arglist (store-var) forms…

これは2番目の、より複雑なdefsetfのフォームである。それは追加の store-var引数を除いてむしろdefmacroと似ている。 formsは、arglistによって記述された引数をともにする access-fnへの呼び出しによって形成された汎変数にstore-var の値を格納するLispフォームを戻すべきである。formssetf メソッドを文書化する文字列で始まってもよい(関数の先頭に現れる 文書文字列と類似している)。

たとえば、defsetfの単純なフォームは以下の簡略表記法である。

(defsetf access-fn (&rest args) (store)
  (append '(update-fn) args (list store)))

戻されるLispフォームは制限されていない仕方でargliststore-varからの引数へアクセスできる; このsetf-methodを起動する setfincfのようなマクロは、評価の明白な順序が 保存されることを確実にするために必要な一時的変数を挿入する。

標準パッケージから引かれた別の例:

(defsetf nth (n x) (store)
  (list 'setcar (list 'nthcdr n x) store))
Special Form: define-setf-method access-fn arglist forms…

これは新しいplaceフォームを作るための最も一般的な方法である。 arglistに記述された引数と一緒にaccess-fnへのsetfが 展開されると、formsは評価されて5項目のリストを 戻さなければならない:

  1. 一時変数のリスト。
  2. 上の一時変数に対応する値フォームのリスト。一時変数は 汎変数上のどんな作用の第1ステップとしてもこれらの値フォームへ 束縛される。
  3. 正確に1個の格納変数のリスト(一般的にgensymへの 呼び出しから得られる)。
  4. 格納変数の内容を汎変数に格納するLispフォーム。一時変数は上述したように 束縛されていると仮定する。
  5. 汎変数の内容にアクセスするLispフォーム。一時変数は束縛されていると 仮定する。

これは同じ名前のCommon Lispマクロとほとんど同じだが、メソッドは5個の 値自身ではなく5個の値のリストを戻す点は異なる。それはEmacs Lispは 多重戻り値のCommon Lispの記法をサポートしていないからである。

もう一度、formsは文書文字列で始まってもよい。

setfメソッドは一時変数に関して最大限に保守的であるべきである。 defsetfによって生成されるsetfメソッドの中で、第2戻り値は単に placeフォーム中の引数リストであり、第1戻り値はgensymによって 生成される対応する一時変数の数のリストである。setfincf のようにこのsetfメソッドを使うマクロは不要とわかった多くの一時変数を 最適化するので、setfメソッド自身を最適化する理由はほとんどない。

Function: get-setf-method place(場所) &optional env

この関数は、defsetfdefine-setf-methodによって以前に 記録された定義を呼び出すことで、placeのためのsetfメソッドを 戻す。結果は上述した5個の値のリストである。あなた自身のincfに 似た修正マクロを作るためにこの関数を使える(実際は内部関数 cl-setf-do-modifycl-setf-do-storeを使う方がよい。 少しだけ使いやすく、かなりの最適化も行なう; 単純な例としてincf 関数のソースコードを調べよ)。

引数envは、get-setf-methodplaceのマクロを展開する 必要がある場合にmacroexpandへ渡される“環境”を指定する。それは マクロへの&environmentか、get-setf-methodを呼んだsetf メソッドから来るべきである。

applysubstringのためのsetfメソッドのソースコードも 参照のこと。それぞれはより単純な場合にget-setf-methodを 呼び出すことによって動作し、それからさまざまな方法で結果を マッサージする。

現代のCommon Lispは関数のsetfの振る舞いを指定するための第2の、 独立した方法を定義する。すなわち、その名前がシンボルではなくリスト (setf name)である“setf関数”である。たとえば、 (defun (setf foo) …)は、setffooに適用する 際に使われる関数を定義する。このパッケージは、現在はsetf関数を サポートしない。まだdefsetfされていないか宣言されていない フォームでsetfを使うことはコンパイル時エラーである; より新しい Common Lispでは、関数(setf func)は後で 定義されるかもしれないのでこれはエラーではないだろう。


Previous: , Up: 汎変数   [Contents][Index]