CLには、lexical な variables と dynamic な variables がある。
Variable Basics
- 型は、valuesが運ぶ。型チェックはランタイムに実施。(dynamicaly typed)
- 全ての型エラーは検知される。(strongly typed)
- 少なくとも概念的には、全てのvaluesは、objectsへの参照である。なので、別の a value を a variable に設定するということは、もとのa value(であるところのa object)に影響を与えない。
- mutable な objects については、それ自体を変更する方法もある。
- 新しいvariablesを導入するには、DEFUNが使える。DEFUNはそれへの呼び出し毎に引数とvaluesのbindingsを生成する。
- arguments = function parameters。
- function parametersは他のvariablesと同じようにobject referencesを保持している。なので、functionのbodyにてfunction parametersに新しいvaluesを割り当てても、他のbindingsには影響はない。
- しかし、functionに渡されたobjectsがmutableであるならば、bodyの中でそのobjectsを変更した場合、その変更はthe callerからは見える。the callerとthe calleeは同じojbectを参照しているから。
- LETもbindingsを作る。
- DEFUNやLETを the binding form と呼ぶ。(それ自身の構造の中でのみ有効なvariablesを導入可能なものは、すべてbinding formと呼ばれる)
- binding forms をネストした場合、同じ名前の variablesについては、外側のbindingsは内側のbindingsによってshadowされる。
- このようにbinding formsのネストの構造が、scopesの区切りになることは、lexical variablesについてもdynamic variablesについても同様である。
Lexical Variables and Closures
lexically scoped variables は、それを導入した binding form の中でしか参照できない。これが基本ルール。しかし、その binding formの評価結果がa function であって、それが、先のlexical scoped variablesを参照していたらどうなるだろう。これもアリである。そのbindingは、評価結果である function object が存在する限りそれにくっついている。これをクロージャという。クロージャが保持しているのはvalues自体ではなくbindingsである。よって、新しいvaluesをクロージャ内で割り当てれば、グローバル変数のように、valuesが変化していきそれを保持できる。
そうか、Schemeを環境モデルで理解するとき 関数定義+環境をクロージャと考えるが、CLではlexically scoped variablesのbindingsを包み込んでいるlambdaのことをクロージャというのか。しかしそうであってもDEFUNでもクロージャは作れるだろ。
CL-USER> (let ((z 10))
(defun foo ()
(setf z (1+ z))
(format t "Z: ~d~%" z)))
FOO
CL-USER> (foo)
Z: 11
NIL
CL-USER> (foo)
Z: 12
NIL
CL-USER>
なんぞ。
複数のクロージャが、ひとつのbindingを捕捉することができる。どれ。
CL-USER> (defparameter *fn2*
(let ((count 0))
(list
#'(lambda () (incf count))
#'(lambda () (decf count))
#'(lambda () count))))
*FN2*
CL-USER> (mapcar #'funcall *fn2*)
(1 0 0)
CL-USER> (funcall (car *fn2*))
1
CL-USER> (funcall (car *fn2*))
2
CL-USER> (funcall (car *fn2*))
3
CL-USER> (funcall (car *fn2*))
4
CL-USER> (mapcar #'funcall *fn2*)
(5 4 4)
CL-USER> (funcall (cadr *fn2*))
3
CL-USER>
なるほど。
ここで小休止。
復帰。
Dynamic, a.k.a. Special, Variables
- global variables を生成するには、DEFVARとDEFPARAMETERがある。
- scopingのルールは、これらについても他と同じ。だから、LETで、*standard-output*を*some-other-stream*にbindした場合、そのbindingsはLETのbodyのみで有効。
- DEFVARとDEFPARAMETERは自動的にspecial宣言していると。
- 思うのだが、top-levelがまさにlexicalになっているMLと比べると、top-levelで名前定義がglobal specialである環境(例えば今やっているCL)とかでは、top-levelでlexicalだdynamicだといってもなんだかわからないものではないのか。
Constants
特になし。
Assignment
- 関数呼び出しは値渡し
Generalized Assignment
- SETF は、user-defined places に使えるように拡張可能。DEFSETF、DEFINE-SETF-EXPANDERなどを使うらしい。
Other Ways to Modify Places
特になし。
0 件のコメント:
コメントを投稿