Special Operator FLET, LABELS, MACROLET
flet ((function-name lambda-list [[local-declaration* | local-documentation]] local-form*)*) declaration* form*
=> result*
labels ((function-name lambda-list [[local-declaration* | local-documentation]] local-form*)*) declaration form* declaration* form*
=> result*
macrolet ((name lambda-list [[local-declaration* | local-documentation]] local-form*)*) declaration* form*
=> result*
function-name - 関数名
name - シンボル
lambda-list - ラムダリスト。 fletとlabelsは通常のラムダリスト。 macroletはマクロラムダリスト。
local-declaration - 宣言式。評価されません。
declaration - 宣言式。評価されません。
local-documentation - 文字列。評価されません。
local-form, form - 暗黙のprogn
result - formの返却値
flet、labels、macroletは、 ローカル関数とローカルマクロを定義し、 そのローカルな定義を使いformを実行します。 formは、現れた順に実行します。
fletとlabelsによって作成された各関数と、 macroletによって作成された各マクロの それぞれのボディ部のform(ただしラムダリストは除く)は、 必要に応じて、 function-nameの関数のblock名かnameを用いて、 暗黙のblockに囲まれます。
ローカル関数とマクロの定義部のリストと、 fletおよびlabelsのボディ部form間における declarationのスコープは、 ローカルの関数定義の本体が含まれません。 ただし例外はlabelsの、 inline、notinline、ftype宣言であり、 これらはローカルの関数定義を参照し、 そのローカル関数の本体に適用します。 つまり、これらの宣言のスコープは、 関数名として影響を与える範囲と同じです。 これらのdeclarationのスコープには、 macroletで定義されたマクロ展開数の本体は含まれません。
flet
fletは、ローカルに名前がある関数を定義し、 それらの定義を束縛とともに一連のformを実行します。 そのようなローカル関数はいくつでも定義できます。 fletのボディ部内では、 fletによって定義されたfunction-nameという名前は、 同名のグローバルに宣言されたものではなく、 ローカルに宣言された関数が参照されます。 fletのスコープ内では、 fletによって定義されている function-nameという名前の、 グローバルのsetf-expanderの定義は適用されません。 これは、(defmethod (setf f) ...)ではなく、 (defsetf f ...)に適用されることに注意してください。 fletによって定義された関数名は、レキシカルな環境内のものであり、 それらのローカルな定義は、ただflet本体内でのみ保持されます。 関数定義の束縛は、flet本体でのみ見ることができ、 それらの定義自身では見ることができません。 関数定義内において、 ローカルの関数名で定義されているものを探すときは、 fletの外側で定義された関数かマクロが参照されます。 fletは、局所的にグローバル関数名を隠蔽することができ、 その新しい定義は、グローバルな定義を参照することができます。 labels
macrolet
macroletは、defmacroと同じフォーマットで、 ローカルマクロの定義を確立します。 macroletの内部では、 macroletによって定義されている function-nameという名前の、 グローバルのsetf-expanderの定義は適用されません。 そうではなく、setfはマクロフォームを展開し、 結果のフォームを再帰的に処理します。 macroletによって定義されたマクロ展開関数は、 macroletのフォームに現れる、 レキシカルな環境によって定義されたものです。 宣言と、macroletとsymbol-macroletの定義は、 macrolet内の、ローカルマクロ定義に影響を及ぼします。 しかしもしローカルマクロ定義から レキシカルな環境内で見ることができる ローカルな変数か関数を参照したときの結果は未定義です。 (defun foo (x flag)
(macrolet ((fudge (z)
; パラメーターxとflagはこの時点ではアクセス不可です。
; flagの参照はグローバル変数の名前になります。
` (if flag (* ,z ,z) ,z)))
; パラメーターxとflagは、ここではアクセス可能です。
(+ x
(fudge x)
(fudge (+ x 1)))))
==
(defun foo (x flag)
(+ x
(if flag (* x x) x)
(if flag (* (+ x 1) (+ x 1)) (+ x 1))))これは、マクロ展開が行われた後です。 xとflagの発生は、 関数fooのパラメータを合法的に参照しています。 なぜなら、これらのパラメータは、 マクロが呼ばれて展開が生成された場所では 見ることができるからです。
(flet ((flet1 (n) (+ n n)))
(flet ((flet1 (n) (+ 2 (flet1 n))))
(flet1 2))) => 6
(defun dummy-function () 'top-level) => DUMMY-FUNCTION
(funcall #'dummy-function) => TOP-LEVEL
(flet ((dummy-function () 'shadow))
(funcall #'dummy-function)) => SHADOW
(eq (funcall #'dummy-function) (funcall 'dummy-function))
=> true
(flet ((dummy-function () 'shadow))
(eq (funcall #'dummy-function)
(funcall 'dummy-function)))
=> false
(defun recursive-times (k n)
(labels ((temp (n)
(if (zerop n) 0 (+ k (temp (1- n))))))
(temp n))) => RECURSIVE-TIMES
(recursive-times 2 3) => 6
(defmacro mlets (x &environment env)
(let ((form `(babbit ,x)))
(macroexpand form env))) => MLETS
(macrolet ((babbit (z) `(+ ,z ,z))) (mlets 5)) => 10
(flet ((safesqrt (x) (sqrt (abs x))))
;; `safesqrt`関数は、二か所で使用されます。
(safesqrt (apply #'+ (map 'list #'safesqrt '(1 2 3 4 5 6)))))
=> 3.291173
(defun integer-power (n k)
(declare (integer n))
(declare (type (integer 0 *) k))
(labels ((expt0 (x k a)
(declare (integer x a) (type (integer 0 *) k))
(cond ((zerop k) a)
((evenp k) (expt1 (* x x) (floor k 2) a))
(t (expt0 (* x x) (floor k 2) (* x a)))))
(expt1 (x k a)
(declare (integer x a) (type (integer 0 *) k))
(cond ((evenp k) (expt1 (* x x) (floor k 2) a))
(t (expt0 (* x x) (floor k 2) (* x a))))))
(expt0 n k 1))) => INTEGER-POWER
(defun example (y l)
(flet ((attach (x)
(setq l (append l (list x)))))
(declare (inline attach))
(dolist (x y)
(unless (null (cdr x))
(attach x)))
l))
(example '((a apple apricot) (b banana) (c cherry) (d) (e))
'((1) (2) (3) (4 2) (5) (6 3 2)))
=> ((1) (2) (3) (4 2) (5) (6 3 2) (A APPLE APRICOT) (B BANANA) (C CHERRY))なし。
なし。
declare, defmacro, defun, documentation, let, 3.1. 評価, 3.4.11. ドキュメント文字と宣言の文脈的な作用
fletでは、再帰関数を定義することはできません。 labelsは、互いに再帰呼出できる関数を定義するのに使うことができます。
もしmacroletがトップレベルフォームのとき、 ボディ部formもまたトップレベルフォームとして処理されます。 3.2.3. ファイルのコンパイルをご確認ください。