LoginSignup
0

More than 5 years have passed since last update.

HSP のループ処理について

Last updated at Posted at 2018-08-19

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「サブルーチンやループのネストが深すぎます」が発生しました。
2018-08-19_105708.png

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「スタック領域のオーバーフローです」がダイアログで表示されました。

2018-08-21_141726.png

参考サイト

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0