nekoie-call/ccパズル解より、Gauche?ではcall/ccパズルが何故動作しないかを追ってみる。他の事をする気にならないので。
- とりあえず、letの実装の部分に秘密がありそう。
- letの動作はsrc/compile.cに記述されているので、このファイルを漁る。
- let変数にbindされている実体は、どうも、1492行目付近の
static ScmSyntax syntax_let = {
{ SCM_CLASS_STATIC_PTR(Scm_SyntaxClass) },
SCM_SYMBOL(SCM_SYM_LET),
compile_let,
(void*)BIND_LET
};
- よく分からないが、どうも、実行される本体は、compile_letのようだ(1420行目付近)。
- ScmObj bindingsに、束縛対象変数及びその定義が入るっぽい様子。
- そして、構文チェックが行われた後、compile_let_familyが呼ばれる様子。
- 引き続き、上記を踏まえて、compile_let_family(1381行目付近)を調べる。
- letrecなら、forループ前に、束縛対象変数を環境に含めておく。
- forループで、束縛対象が一件ずつ処理される。
- この段階で、SCM_CAR(varp)が束縛対象変数名、SCM_CAR(valp)には束縛対象の定義λが入っている
- そして、この環境で、compile_int(束縛対象の定義λのコンパイル)が実行され、その結果をvalに入れる
- 大変そうなので、compile_intは追わないが、実行した結果、add_srcinfoの返り値か、SCM_NILか、SCM_LIST1(form)の返り値が返るようだ
- valはADDCODEマクロによって、常にメモリ上に確保される???不明…‥
ScmObj val = compile_int(SCM_CAR(valp), newenv, SCM_COMPILE_NORMAL, depth);
ADDCODE(val);
ADDCODE1(make_lset(0, nvars-count-1));
- そして、その後、let*ならforループ中で、letならforループ後で、束縛対象変数を環境に含めている。
- そして、この環境を使って、bodyをコンパイルする。
- しかし、実際にこのコードが実行されるのは、全てがコンパイル完了してからなので、結果として、「変数の位置だけ確保」→「そこに、束縛対象の定義λの実行結果を代入」という、set!的動作になってしまっている。
- その為、nekoie-call/ccパズル解のように、
- yin定義(yin=yin0)
- yang定義(yang=yang0)
- 継続を辿って、yin定義中(yin0)に戻る
- yin再定義(yin=yin1=yang0)
- yang再定義(yang=yang1)
- 継続を辿って、yang定義中(yin1=yang0)に戻る
- と、yang0に戻っても、既にyinはyin1によって上書きされているので、guileとは異なる動作をしている、となる。
- どうやら、コレを解決するには、単にコードの順を入れ替えるだけではなく、結構な修正が必要そうだ…‥。
- body部分も、bind部分と同じように、シンタックスチェックだけ先に済まし
- 実際のコンパイルは、このコードが実際に評価される時まで先延ばしにして遅延評価風にすれば、問題ない
- …‥と思ったが、よく考えたら、それでは、毎回そのletが実行される度に、コンパイルが行われる事になってしまい、パフォーマンスが落ちる…‥。
- これ以上考えても、自分には解決できそうになさそうだ。
最終更新 : 2004/04/10 20:00:35 JST