#SUB で始まるセクションです。ここではユーザーが作成するサブルーチンを定義します。
サブルーチンは16384個まで定義可能です。それ以上定義した場合、コンパイルエラーになります。
定義の仕方の形式は以下の通りです。
#SUB サブルーチン名([VAR] [引数[,[VAR] 引数...]) 複文
サブルーチン名はA-Z,0-9,_で構成される、60文字までの文字列です。ただし、先頭に数字は使用できません。また、システムで予約されている判定式や手続きと同じ名前は使用できません。引数は省略することもできます。
サブルーチン名は大文字・小文字を区別しません。
サブルーチンは16384個まで定義可能です。それ以上定義した場合、コンパイルエラーになります。
複文の詳細は後述します。ここでは、「実行したい文を書きならべたもの」という程度の認識にとどめておいてください。
引数は#GLOBALVARと同じ命名法で使用します。しかし、初期値を設定することはできません。また、#GLOBALVARで宣言した名前と同じものを使用できますが、その場合はそのサブルーチン中ではその名前を持つグローバル変数・文字列が使用できなくなります。
例) #SUB Foo(#test) { ... } #SUB Bar($op,#code) { ... }
引数の宣言に配列を使用することもできます。ただし、下記のように記述し、『添え字を宣言しないで』ください。
例) #SUB Foo(#oop[]) { ... } // 正しい #SUB Bar(#too[4]) { ... } // だめ!!
引数に指定した配列の使用方法は後述します。
引数は、そのサブルーチン内で通常の変数・文字列として扱えますが、値を変更しても、呼び出しもとの変数には影響を与えません。これを「値引数」と呼びます。
例) #SUB Foo(#val) { IF (#val==10) THEN { ShowMessage('こっちになります','TEST',0); } ELSE { ShowMessage('こっちにはなりません,'TEST',0); }; #val=-1; IF (#val==-1) THEN { ShowMessage('こっちになります','TEST',0); } ELSE { ShowMessage('-1のはずですが?,'TEST',0); }; } #KEY T { var #test; #test=10; // ここでは#test=10 Foo(#test); // Fooをよびだす。 IF (#test==10) THEN { // #testは10のまま ShowMessage('10のまま','TEST',0); } ELSE { ShowMessage('こっちにはならない','TEST',0); }; }
サブルーチン内での変更を、呼び出しもとに反映したい場合、引数の宣言で、引数の前に VAR をつける必要が有ります。
例) #SUB Foo(var #test) { ... } #SUB Bar(var $op,var #code) { ... }
この場合も、引数はそのサブルーチン内で通常の変数・文字列として扱うことができます。値を変更した場合は、呼び出し元の変数の値も変化します。これを「変数引数」とよびます。
例) #SUB Foo(var #val) { IF (#val==10) THEN { ShowMessage('こっちになります','TEST',0); } ELSE { ShowMessage('こっちにはなりません,'TEST',0); }; #val=-1; IF (#val==-1) THEN { ShowMessage('こっちになります','TEST',0); } ELSE { ShowMessage('-1のはずですが?,'TEST',0); }; } #KEY T { var #test; #test=10; // ここでは#test=10 Foo(#test); // Fooをよびだす。 IF (#test==10) THEN { // #testは-1に変わっている ShowMessage('こっちにはならない','TEST',0); } ELSE { ShowMessage('-1になっている','TEST',0); }; }
!!注意!!
値の変更が、実際に反映されるのはそのサブルーチンが終了してからです。これはグローバル変数・文字列を引数として渡したときは、特に注意する必要が有ります。
例) #GLOBALVAR #glbNUM; #SUB Foo(var #a) { #a=20; IF (#glbNUM==20) THEN { ShowMessage('まだ反映されていないはず','Miss',0); } ELSE { ShowMessage('こちらになる(10のまま)','OK',0); }; } #KEY T { #glbNUM=10; Foo(#glbNUM); // Fooが終了した時点で#glbNUMに反映される IF (#glbNUM==20) THEN { ShowMessage('こちらになる','OK',0); } ELSE { ShowMessage('反映されたはずなのに?','Miss',0); }; }
サブルーチンを呼び出すとき、サブルーチンの引数が「値引数」か「変数引数」かによって、渡せる数値・文字列が違います。
例) #GLOBALVAR #glVAL; #SUB Foo(#n,$s) { ... } #SUB Bar($newsysytem) { Foo(0,$newsystem); } // OK #KEY T { var #i,$name,#t[3],$rou[6]; Foo(#i,$name); // OK Foo(33,#_CUR_FILENAMEEXT); // OK Foo(#_FILENUM,'This is Only a test.'); // OK Foo(#glVAL,'test'); // OK Foo(#i*2+1,'this is '+$name); // OK Foo(#t[1],$rou[2]); // OK }
例) #GLOBALVAR #glVAL; #SUB Foo(var #n,var $s) { ... } #SUB Bar(#gd,$newsysytem) { Foo(#gd,$newsystem); } // OK #SUB Goo(#nuu[],$rick[]) { Foo(#nuu[1],$rick[1]); } // OK #KEY T { var #i,$name,#t[3],#rou[6]; Foo(#i,$name); // OK Foo(#t[1],$rou[2]); // OK Foo(33,$name); // 定数は渡せない Foo(#i,$_CUR_ATTR); // システム文字列は渡せない Foo(#glVAL,'test'); // 文字列定数は渡せない Foo(3+#i*2,'$name); // 計算式は渡せない Foo(#i,$name+' is.'); // 文字列式は渡せない }
配列を引数に宣言したサブルーチンを使用するとき、引数には[]をつけないで渡してください。
例) #SUB Foo($LockTable[]) { ... } #KEY SHIFT+L { var $LockList[3]; .... Foo($LockList); // []はつけないでFoo()に渡す }
配列を値引数又は変数引数にした場合、それらの配列を使用するには、GetMaxIndex手続きを使用して、添え字の最大値を取得してください。添え字の最大値を越えて使用しようとすると例外が発生します(except文ではつかまえられません)。
例) #SUB Foo(#Popop[]) { var #max,#i; GetMaxIndex(#max,#Popop); // []が無いことに注意! FOR #i=1 TO #max DO { ... #Popop[#i]=#nanotka*#kantoka; // 添え字は1から#maxまで!! ... }; }
値引数の配列を宣言した場合、そのサブルーチンには配列定数を渡すことができます。
例) #SUB Foo($Goo[]) { ... } #SUB Bar() { Foo(['1st','2nd','3rd']); }; // OK
配列定数は [ (文字列)定数, ... , (文字列)定数 ] と記述します。変数引数の配列を宣言した場合は渡せません。
例) #SUB Foo(var $Goo[]) { ... } #SUB Bar() { Foo(['1st','2nd','3rd']); }; // NG
自身の定義の中で自分を呼び出すことが可能です。
再帰呼び出しを行うときは、停止条件に十分注意してください。あっというまにスタックを食い潰します。
例) #SUB NestSub(var #val,#s) { // ありがちなフィボナッチ数列:- var #w; IF (#s<=0) THEN { #val=0; } ELSEIF (#s==1) THEN { #val=1; } ELSE { NestSub(#w,#s-1); // 再帰呼び出し #val=#w+#s; }; }
もしスタックを食い潰してしまったら、一度SASFを終了して再度立ち上げてください。
通常、サブルーチンは定義が終わっていなければ使用することができません。しかし、名前と引数の定義だけ先に行い、実装を後で行うためのキーワードがFORWARDです。
#SUB routine名(引数..); FORWARD; 1 2 3
と宣言することで、サブルーチンの名前だけ先に宣言できます。引数の括弧の後ろ(1)に ; を、その後(2)に FORWARD を、更に後ろ(3)に ;をつけてください。
例) #SUB Foo(var #i); FORWARD; #KEY R { Foo(); } #SUB Foo(var #i) { // Fooの実装 }
FORWARD宣言したサブルーチンは、必ずその宣言以降のどこかで実態を定義してください。定義しわすれた場合、コンパイラが「実体が無い」旨を表示してコンパイルを中止します。
実体を定義する場合、『引数の型や順番はFORWARDしたものと合わせる必要があります』。
例) #SUB Foo(var $rrr, #Toto[]); forward; ... #SUB Foo(var $rrr, #Toto[]) { ... } // OK!! #SUB Bar(#sss,$w); forward; #SUB Bar(#sss,$w[]) { ... } // NG !! 2つ目の引数の型が違う
このとき、合わせるのは『型』と『順番』であって『変数名』は別でもかまいません。
例) #SUB Foo($Works[],var #cnt); forward; ... #SUB Foo($list[],var #maxcount) { ... } // OK!!
なお、実体の方で使う引数の名前は、実体で宣言している引数の名前を使用してください。上記の例ならば $list[] と#maxcountを使用してください。