複雑な関数は上に置け!

harayoki

2007年10月11日 04:37




最近はなかなか多忙でINできないので…
lslプログラムをSciTE上で作ってみたりしていました。
(SciTEは汎用プログラムエディターです…詳しくはココを)
INする時間がなくても、コンパイルが通るかどうかまではSciTEだけで作れますし。

で、だんだんプログラムが長くなってくると。コンパイルチェック時に
Parser stack depth exceeded
などという、エラーが出てしまう事が多くなりました。

このエラーの挙動がなんだかよくわからい感じで…
気持ち悪かったので調べてみました。

まずlslプログラムは複雑な状態になると…
…簡単に言うと150以上の階層ができてしまうとエラーになってしまうようです。
変数・関数の宣言はそれぞれ1階層増えたとみなされます。

うーん、簡単じゃないですね。
実は、自分もよくわかってません。が、なんとなくはわかりました。

ここで、エラーになる例を1つ
integer I01;
integer I02;
integer I03;
integer I04;

: 中略

integer I148;
integer I149;
integer I150;
default{
state_entry(){
}
}

このように変数を150個宣言すると、150個目でエラーになり、コンパイルが通りません。
まあ、こんなバカなプログラムは書かないでしょうが。

これは変数の宣言が関数の宣言になってもほぼ同じです。
関数の場合は(3つへって)147関数を宣言するとエラーになります。

次の例です。
F01(){}
F02(){}
F03(){}
F04(){}

: 中略

F131(){}
F132(){
if(TRUE){
if(TRUE){
if(TRUE){}
}
}
}
default{
state_entry(){
}
}

最後の関数がこういう感じの内容の場合、ぐっと減って132個目の関数宣言でエラーになります。
(注:式の意味は滅茶苦茶です)

あと残り約15階層はどこでカウントされてるのか。
それは
if(TRUE){
if(TRUE){
if(TRUE){}
}
}

の部分です。

しかし試しにこの行のifネストを1個減らしてみると
if(TRUE){
if(TRUE){
}
}

エラーにはなりません。

合間に式を沢山はさみこんでも
F132(){
if(TRUE){
if(TRUE){
i++;
i++;
i++;
i++;
i++;
i++;
}
}
}

エラーはでません。

関数宣言、for,if,whileなど、{}で囲む部分が増える箇所で
式の階層数がいくつかカウントしまうようで…
特にforが階層数を多く消費するようです。

つまりlslソースの後半に、ネストが激しい関数(if,for,whileなどが多く書いてある関数)があると
その行でParser stack depth exceeded =コンパイラがソースを解析中にスタックの限界の深さを超えてしまいましたなるエラーが出てしまう可能性が大きいわけです。

しかし、いったんネストが閉じられると
もとの階層数まで使用数が戻えいます。
その関数でエラーにならなければその後変数や関数宣言が続けられるわけです。

具体的には
さっきの例を

F131(){}
F132(){
if(TRUE){
if(TRUE){
if(TRUE){}
}
}
}


F132(){
if(TRUE){
if(TRUE){
if(TRUE){}
}
}
}
F131(){}

と順序を逆にするだけで、エラーにならなくなります。

F31ではまだ使用階層数に余裕があるからです。

この結果から、タイトルにあるように
長いlslを記述する際は、複雑な関数をソースの上部に配置しろ
というテクニック?が導かれます。

同様に
長いlslを記述する際は、無駄な変数を宣言するな
長いlslを記述する際は、無駄な関数を宣言するな
というテクニック?も導かれますね。

1度しか使わないものでも、定数を宣言したり、意味ごとに関数にまとめるのは
scriptの見通しを良くするのに大事なことなのですが…
上記のテクを適用すると、とてもプログラムが汚くなります…。
なんだかなあ。


いや、そもそも
lslでソースが長くなるような複雑な処理は書くな
という事なのかもしれません!

トホホって感じですが。


記事書くのにつかれた。今日もSLで遊ばずに…寝ます。


lsl、ECMAscriptとかで実装しなおされてくれないかなあ。
script