2008年10月21日火曜日

値渡し?

ぼやぼや考えていたら、さらにわかっているようでわかっていないことがわかった。
CLの場合、他の言語の値渡しとか参照渡しとかと同列に考えるのは危険な気がする。Symbolがいるから。

さて、関数はeagerなので、とにかくその引数は一回評価された上で関数の処理対象となる。評価された状態が何なのか、ということだ。先日の例では、Symbol hogeが値として指しているObjectがそれにあたる。そしてその後に起こるのは、バインディングだ。そのObjectがそのときに生成された環境内の変数xにバインドされる。このxはシンボルではない。Static (Lexical) Environmentに属するものであり、ANSIの範囲ではSymbolのようにxそれ自体にアクセスすることはできない。


CL-USER(15): (setq hoge 1)
1
CL-USER(16): (defun s-name (x)
(symbol-name x))
S-NAME
CL-USER(17): (s-name hoge)
Error: Attempt to access the name field of 1 which is not a symbol.
[condition type: TYPE-ERROR]

Restart actions (select using :continue):
0: Return to Top Level (an "abort" restart).
1: Abort entirely from this (lisp) process.
[1] CL-USER(18): :pop
CL-USER(19):


ところで、この関数を意図通りに動かしたいなら、Symbol Objectを渡せばいいだけだ。

CL-USER(19): (s-name 'hoge)
"HOGE"

C言語でいう、値渡しや参照渡しとはどうも違うような気がする。Lisp Object渡しというか何というか。そう思わせるのはSymbol自体がユーザがさわれるLisp Objectだからかな。
しかし、C言語の用語で言えば、やはりCLの関数は参照渡しなのだろう。バインドされるだけで、コピーは作られないから。

で、マクロが何かというと、こちらはlazyなので、そのままSymbol(より一般的にはS式)として評価されずに渡される。(ただし単純にlazyというのは乱暴かもしれない。より正確にはMacro expansion timeに、その時点の環境に準拠しつつ展開されるということ)

なので、こういう芸当ができる。


CL-USER(34): (defmacro s-name (s)
`(symbol-name (quote ,s)))
S-NAME
CL-USER(35): (s-name hoge)
"HOGE"
CL-USER(36):

これは関数にはできない。

こつこつ。

0 件のコメント: