repeat や foreach では cnt 変数にループ回数がカウントされていきます。
foreach はモジュール型変数の配列を扱う際に便利だったりするのですが、その内部で repeat や foreach を使ったら cnt が上書きされて処理が正常にならないのではないかと心配になり、実際にテストしてみました。
結論からいうと、 repeat や foreach は入れ子にしてもそれぞれ独立した cnt を参照するので問題ありませんでした。
以下は、ループについて実際にテストした結果とその考察です。
テスト環境は HSP3.5, windows 7 personal 64bit です。
#repeat と foreach の cnt は入れ子にしても大丈夫
#module
#deffunc testRepeat1
repeat 2
_value1 = 1
repeat 3
_value1 = -1
logmes "repeat(2) cnt = " + cnt // 3回呼び出され、 0, 1, 2 が表示される
loop
logmes "_value1 = " + _value1 // -1 が表示される。1層目のrepeatと2層目のrepeatでは同じ _value を参照している。
logmes "repeat(1) cnt = " + cnt // 1周目は 0 、2周目は 1 が表示される。1 層目の repeat と 2 層目の repeat では独立した cnt をそれぞれ参照している。
loop
return
#global
testRepeat1
logmes によるログ出力は以下のとおりです。
repeat(2) cnt = 0
repeat(2) cnt = 1
repeat(2) cnt = 2
_value1 = -1
repeat(1) cnt = 0
repeat(2) cnt = 0
repeat(2) cnt = 1
repeat(2) cnt = 2
_value1 = -1
repeat(1) cnt = 1
これをみると、 repeat 1 層目と 2 層目の cnt は独立していてそれぞれの値を保っています。
ですが、 repeat ~ loop 間はスコープが変わるわけではなく、 _value1 のようなユーザ定義の静的変数は層に関係なく同じものが参照されます。
repeat, foreach のネスト回数
repeat, foreach は入れ子にできますが、その階層の数は 31 までです。
参照:HSPTV!掲示板
現在の階層については looplev で確認できます。
#module
#deffunc testRepeatLevel
#define R repeat 1:logmes "looplev = " + looplev:
#define L loop:
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R
L L L L L L L L L L L L L L L L L L L L L L L L L L L L L L L L
#undef R
#undef L
return
#global
testRepeatLevel
コンソールでは 1 ~ 31 階層までの31回のネストまで実行されたことが表示されています。
looplev = 1
looplev = 2
looplev = 3
(略)
looplev = 29
looplev = 30
looplev = 31
それ以降(32番目のR,N)は、Error9「サブルーチンやループのネストが深すぎます」が発生しました。
#foreachでモジュール型変数の配列を扱う
repeat, foreach は階層が 31 階層までという制限はありますが、 foreach はモジュール型変数の配列を扱う際に便利です。
配列の一部の要素を delmod した際に、それをスキップしてくれます。
#module Person id_, name_
#modinit int _id, str _name
id_ = _id
name_ = _name
logmes "modinit " + serialize(thismod)
return
#modterm
logmes "modterm " + serialize(thismod)
return
#modcfunc local serialize
return "Person," + id_ + "," + name_
#global
#module
#deffunc testModuleInstanceArray1
// モジュール型変数の配列の要素に delmod すると要素自体が減るのか、変わらないのかのテスト
newmod _persons, Person, 1, "test1-1"
logmes "length(_persons) = " + length(_persons)
newmod _persons, Person, 2, "test1-2"
logmes "length(_persons) = " + length(_persons)
newmod _persons, Person, 3, "test1-3"
logmes "length(_persons) = " + length(_persons)
delmod _persons(1)
logmes "length(_persons) = " + length(_persons) // 3 が表示される。delmodで配列の要素自体は除去されず、各変数への要素番号は変わらない。
foreach _persons // delmod された要素番号 1 はスキップされ、ループ内の処理は2回行われ cnt は 0, 2 となる。
logmes "(" + cnt + ")" + serialize@Person(_persons(cnt))
loop
//logmes "(" + 1 + ")" + serialize@Person(_persons(1)) // delmod された要素の関数を呼び出すと、「モジュール型変数の指定が無効です」エラーになる。
return
#global
testModuleInstanceArray1
結果は次のようになります。
modinit Person,1,test1-1
length(_persons) = 1
modinit Person,2,test1-2
length(_persons) = 2
modinit Person,3,test1-3
length(_persons) = 3
modterm Person,2,test1-2
length(_persons) = 3
(0)Person,1,test1-1
(2)Person,3,test1-3
同じ変数に newmod を繰り返すと要素が1つずつ追加されていきます。
一部の要素を delmod した場合も、配列のサイズや要素の順番には変化はありません。
foreach では delmod された要素はスキップされます。
例えば、上の例では foreach 内の処理は2回呼び出され、delmod された 1 番目をのぞいた 0, 2 の cnt の値の処理が行われます。
当然ながら _persons(1) のように delmod した要素を使おうとすると「モジュール型変数の指定が無効です」エラーになります。
#for ~ next, while ~ wend はマクロ
HSPエディタのヘルプ > HSP検索キーワードなどで表示できる HSP Document Library で for を調べると、マクロによって実現されていると書かれています。
そのため、 repeat, foreach で使えた break, continue は使えず、そのかわりに _break, _continue でマクロループを脱出できます。
#サブルーチンは511回までネストできる
サブルーチンの階層は sublev システム変数で確認できます。
次のテストコードで何階層まで呼べるか確認しました。
*sub1
logmes "sublev = " + sublev
gosub *sub1
結果のログ出力は以下です。
sublev = 0
sublev = 1
sublev = 2
...(略)...
sublev = 508
sublev = 509
sublev = 510
この直後にError29「スタック領域のオーバーフローです」がダイアログで表示されました。
#参考サイト