Skip to content
Silica edited this page Sep 4, 2012 · 8 revisions

#Closure int x = 1; c = $(){print(x);}; c(); // 1

##$演算子 CLOSURE命令 $は単体の演算子である
ローカル変数への参照を無名関数に渡したに過ぎない

$aとすればローカル変数aに対し、ローカル変数への参照をメンバとして登録する

int x = 1;
$a;
print(a.x); // 1

ここでいう参照とはスクリプト内の参照型ではなく
内部的な機能による変数の共有である
外のxとa.xは全く同じ変数として動作する

尚、参照ではなくコピーを渡すことも出来る
コンパイルオプションで切り替える

##注意点 クロージャ命令は命令を実行した時のローカル変数を、対象のメンバとして登録する
対象のメンバというのはつまり、内側から見ればstatic変数のことなので
クロージャを重ねた場合には伝播しない

list = (1,2,3);
int total = 0;
foreach(list, $(x){
	total += x;
});
print(total); // 6

これはいい

list = (1,2,3),(4,5,6),(7,8,9);
int total = 0;
foreach(list, $(x){
	// ここからはstatic変数としてtotalを参照する事が出来るが
	foreach(x, $(y){
		// static変数は渡されない為にここからは見えなくなっている
		total += y; // よってこのtotalは外から渡されてきたtotalではなく
		// この中で新たに作られたローカル変数である
	});
});
print(total); // 0

回避するには一旦ローカル変数に渡す必要がある

list = (1,2,3),(4,5,6),(7,8,9);
int total = 0;
foreach(list, $(x){
	ref t = total;
	foreach(x, $(y){
		t += y;
	});
});
print(total); // 45

変える(static変数も渡す様にする)かもしれない

また別種の問題として、代入の際にコピーを作る問題もある

int x = 1;
c = $(y){x = y;};
c(10);
print(x); // 1

何故か
(y){x = y;}という関数リテラルにxを渡す
仮にこれにanonymousという名前を仮定して考えれば

anonymous = (y){x = y;};
anonymous.x = &x; // 厳密に言えば(内部)参照を渡すのだが

この様な動作と同じである
ここで次にc = anonymous;という操作をしていることを考えれば答えは自ずと見えてくる.
anonymousはメンバごとコピーされる、その際に内部参照は消えて値のみがコピーされる.
回避するにはref c = $(y){x = y;};とするか、
クロージャを作るタイミングをcにコピーした後にする、つまり

c = (y){x = y;};
$c;
// または
$(c = (y){x = y;});

foreachに渡す場合は気にしなくてよい(自動的に参照を渡す)が
まあそのうち考える

Clone this wiki locally