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))
(=> SET-MIDDLEGUY
v) defsetf middleguy set-middleguy) => MIDDLEGUY
(setq a (list 'a 'b 'c 'd)
(list 'x)
b (list 1 2 3 (list 4 5 6) 7 8 9)) => (1 2 3 (4 5 6) 7 8 9)
c (setf (middleguy a) 3) => 3
(setf (middleguy b) 7) => 7
(setf (middleguy (middleguy c)) 'middleguy-symbol) => MIDDLEGUY-SYMBOL
(=> (A 3 C D)
a => (7)
b => (1 2 3 (4 MIDDLEGUY-SYMBOL 6) 7 8 9) c
defsetf
の長いフォームの使用例。
defsetf subseq (sequence start &optional end) (new-sequence)
(progn (replace ,sequence ,new-sequence
`(:start1 ,start :end1 ,end)
=> SUBSEQ
,new-sequence)) 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)
(=> XY
`(set-xy ,store 'x ,x 'y ,y)) 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)1) => NIL
(xy 'x setf (xy 'x 1) 1) => 1
(1) => 1
(xy 'x let ((a 'x) (b 'y))
(setf (xy a 1 b 2) 3)
(setf (xy b 5 a 9) 14))
(=> 14
0 'x 1) => 1
(xy 'y 1 'y 2) => 3 (xy 'x
なし。
なし。
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
によって課されている制限に ふさわしくないような状況を処理するために使われ、 ユーザーに対して追加の制御を与えます。