% Macro DEFSETF
Macro DEFSETF
短いフォーム:
defsetf
access-fn update-fn [documentation]
=> access-fn
長いフォーム:
defsetf
access-fn lambda-list (store-variable*)
[[declaration* |
documentation]] form*
=> access-fn
access-fn - 関数かマクロの名前のシンボル
update-fn - 関数かマクロの名前のシンボル
lambda-list - defsetf
ラムダリスト
store-variable - シンボル(変数名)
declaration - 宣言式。評価されません。
documentation - 文字列。評価されません。
form - フォーム
defsetf
は、比較的簡単な場合のplace
のフォーム(access-fn ...)
を
どのようにしてsetf
で適用するか定義します
(より一般的なアクセスの機能については、
define-setf-expander
をご確認下さい)。
access-fnという関数かマクロは、
それらの引数の全てを評価しなければなりません。
defsetf
は、「短いフォーム」、「長いフォーム」と呼ばれる
2つのフォームのひとつを取ることができます。
それらは2番目の引数の型によって区別されます。
短いフォームを使うとき、update-fnは関数かマクロの名前でなければならず、
それはaccess-fnよりひとつ多い引数を取ります。
もしsetf
に与えられたplace
でaccess-fnが呼ばれたとき、
access-fnへ与えられたすべての引数と
最後の引数として新しい値を追加したものを
update-fnに指定して呼びし展開します
(最後の新しい値はupdate-fnによって返却する必要があります)。
defsetf
の長いフォームは、defmacro
に似ています。
lambda-listはaccess-fnの引数を定義します。
store-variableはplace
へ格納する値か多値を定義します。
bodyはaccess-fnの呼び出しの
setf
への展開を計算する必要があります。
展開関数はdefsetf
フォームが現れたところと
同じレキシカルな環境上で定義されます。
formを評価している間、lambda-listとstore-variableの変数は、
gensym
やgentemp
によって生成された一時変数に束縛され、
setf
の展開によってこれらのサブフォームの値に束縛されます。
この束縛によって、評価順の問題を考慮することなく
formを書くことができるようになります。
defsetf
は、このような場合において可能であれば、
一時変数が最終的な値を最適化するように調整します。
defsetf
のコード本体は、
暗黙的にaccess-fnという名前のblock
で囲まれます。
defsetf
はplace
のサブフォームが
正確に一度だけ評価されることを保証します。
documentationは、ドキュメント文字の種別setf
として
access-fnに割り当てをします。
もしdefsetf
フォームがトップレベルフォームとして現れたときは、
コンパイラは、そのファイルの後でsetf
が呼ばれたときに展開が行われるよう、
setf-expanderを有効にしなければなりません。
もし同じファイルの後でplace
がaccess-fnを使用するときは、
ユーザーは、もしあるならformをコンパイル時に
評価できることを保証しなければなりません。
コンパイラーは、get-setf-expansion
の環境引数が
マクロの環境パラメータの値を受け取った場合、
コンパイル時にこれらのsetf-expanderを
利用できるようにしなければなりません。
次の式の効果は、Common Lispシステム内に構築されています。
(defsetf symbol-value set)
これは(setf (symbol-value foo) fu)
というフォームが
(set foo fu)
に展開されます。
次の文に注意してください。
(defsetf car rplaca)
この文は正しくはなく、
なぜならrplaca
は最後の引数を返却しないからです。
(defun middleguy (x) (nth (truncate (1- (list-length x)) 2) x)) => MIDDLEGUY
(defun set-middleguy (x v)
(unless (null x)
(rplaca (nthcdr (truncate (1- (list-length x)) 2) x) v))
v) => SET-MIDDLEGUY
(defsetf middleguy set-middleguy) => MIDDLEGUY
(setq a (list 'a 'b 'c 'd)
b (list 'x)
c (list 1 2 3 (list 4 5 6) 7 8 9)) => (1 2 3 (4 5 6) 7 8 9)
(setf (middleguy a) 3) => 3
(setf (middleguy b) 7) => 7
(setf (middleguy (middleguy c)) 'middleguy-symbol) => MIDDLEGUY-SYMBOL
a => (A 3 C D)
b => (7)
c => (1 2 3 (4 MIDDLEGUY-SYMBOL 6) 7 8 9)
defsetf
の長いフォームの使用例。
(defsetf subseq (sequence start &optional end) (new-sequence)
`(progn (replace ,sequence ,new-sequence
:start1 ,start :end1 ,end)
,new-sequence)) => SUBSEQ
(defvar *xy* (make-array '(10 10)))
(defun xy (&key ((x x) 0) ((y y) 0)) (aref *xy* x y)) => XY
(defun set-xy (new-value &key ((x x) 0) ((y y) 0))
(setf (aref *xy* x y) new-value)) => SET-XY
(defsetf xy (&key ((x x) 0) ((y y) 0)) (store)
`(set-xy ,store 'x ,x 'y ,y)) => XY
(get-setf-expansion '(xy a b))
=> (#:t0 #:t1),
(a b),
(#:store),
((lambda (&key ((x #:x)) ((y #:y)))
(set-xy #:store 'x #:x 'y #:y))
#:t0 #:t1),
(xy #:t0 #:t1)
(xy 'x 1) => NIL
(setf (xy 'x 1) 1) => 1
(xy 'x 1) => 1
(let ((a 'x) (b 'y))
(setf (xy a 1 b 2) 3)
(setf (xy b 5 a 9) 14))
=> 14
(xy 'y 0 'x 1) => 1
(xy 'x 1 'y 2) => 3
なし。
なし。
documentation
,
setf
,
define-setf-expander
,
get-setf-expansion
,
5.1. 一般化された参照,
3.4.11. ドキュメント文字と宣言の文脈的な作用
formには正しい値(store-variableの値か多値)の
返却のための規定を含めなければなりません。
これはdefsetf
によってではなくformによって処理します。
なぜなら、多くの場合この値は、その場所に保存して正しい値を返す関数を
同時に呼び出すことによって、余分なコストをかけずに返却できるからです。
setf
のaccess-fnの呼び出しは、
access-fnの全ての引数もまた評価されます。
それはどの引数も特別に扱うことができません。
つまりdefsetf
は、(ldb field reference)
のような
バイトの一般参照への格納方法を記述することができません。
define-setf-expander
は、
defsetf
によって課されている制限に
ふさわしくないような状況を処理するために使われ、
ユーザーに対して追加の制御を与えます。