Macro DEFSETF

UP


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に与えられたplaceaccess-fnが呼ばれたとき、 access-fnへ与えられたすべての引数と 最後の引数として新しい値を追加したものを update-fnに指定して呼びし展開します (最後の新しい値はupdate-fnによって返却する必要があります)。

defsetfの長いフォームは、defmacroに似ています。 lambda-listaccess-fnの引数を定義します。 store-variableplaceへ格納する値か多値を定義します。 bodyaccess-fnの呼び出しの setfへの展開を計算する必要があります。 展開関数はdefsetfフォームが現れたところと 同じレキシカルな環境上で定義されます。

formを評価している間、lambda-liststore-variableの変数は、 gensymgentempによって生成された一時変数に束縛され、 setfの展開によってこれらのサブフォームの値に束縛されます。 この束縛によって、評価順の問題を考慮することなく formを書くことができるようになります。 defsetfは、このような場合において可能であれば、 一時変数が最終的な値を最適化するように調整します。

defsetfのコード本体は、 暗黙的にaccess-fnという名前のblockで囲まれます。

defsetfplaceのサブフォームが 正確に一度だけ評価されることを保証します。

documentationは、ドキュメント文字の種別setfとして access-fnに割り当てをします。

もしdefsetfフォームがトップレベルフォームとして現れたときは、 コンパイラは、そのファイルの後でsetfが呼ばれたときに展開が行われるよう、 setf-expanderを有効にしなければなりません。 もし同じファイルの後でplaceaccess-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によって処理します。 なぜなら、多くの場合この値は、その場所に保存して正しい値を返す関数を 同時に呼び出すことによって、余分なコストをかけずに返却できるからです。

setfaccess-fnの呼び出しは、 access-fnの全ての引数もまた評価されます。 それはどの引数も特別に扱うことができません。 つまりdefsetfは、(ldb field reference)のような バイトの一般参照への格納方法を記述することができません。 define-setf-expanderは、 defsetfによって課されている制限に ふさわしくないような状況を処理するために使われ、 ユーザーに対して追加の制御を与えます。


TOP, Github