同じようなものは、固めておくのが良です。これは、プログラミングについても同じことがいえます。
前回に、#module の機能について解説したので、今回は、モジュールを使った、もっと実用的な話に移ります。
1つの #module と、1つの目的のための処理を行うユーザ定義命令・関数が #module の中にいくつか定義されているファイルを、(HSPでは) モジュールと呼びます。
例として、次のような「文字列切り出しモジュール(strtok)」を考えます。
// module - strtok (string tokenize) // 簡易版 #ifndef IG_MODULE_STRTOK_AS #define IG_MODULE_STRTOK_AS #module strtok //------------------------------------------------ // strtok バッファを設定する // // @prm sBuf : 区切られるテキスト // @prm delimiter : 区切り文字列 //------------------------------------------------ #deffunc strtok_setBuffer str sBuf, str delimiter stt_buf = sBuf stt_delimiter = delimiter stt_index = 0 stt_lenBuf = strlen(stt_buf) stt_lenDelimiter = strlen(stt_delimiter) return //------------------------------------------------ // 文字列を切り出す //------------------------------------------------ #defcfunc strtok_getnext if ( stt_bFinished ) { // 既に完了している return "" } // 次の区切り文字を検索 i = instr( stt_buf, stt_index, stt_delimiter ) if ( i < 0 ) { i = stt_lenBuf - stt_index // 残りすべて } // 結果 sResult = strmid( stt_buf, stt_index, i ) stt_index += i stt_index += stt_lenDelimiter // 区切り文字列を飛ばす return sResult //------------------------------------------------ // すでに取り出しが完了しているか //------------------------------------------------ #defcfunc strtok_isFinished return (stt_lenBuf <= stt_index) #global // サンプル・スクリプト #if 0 // 商品データ (という設定) // 「商品名,単価,在庫数」ということにする buf = {" リンゴ,100,30 みかん,170,12 ぶどう,1600,2 "} // strtok バッファに設定 strtok_setBuffer buf, "\n" // すべての行について、1行ずつ処理 repeat if ( strtok_isFinished() ) { break } data = strtok_getnext() mes "#" + cnt + " " + data loop stop #endif
最後の方にある「#if 0」から「#endif」は、使用例=サンプルです。「0」を真 (= 0 以外の数値) に変更すると、実行できます。これは、モジュールにサンプルを書くときの常套手段です。
まずは、これを実行してみて、動作を確認してみてください。
……これで、おおかたの使い道は分かると思います。getstr と同じようなことをしているだけですね (爆)。
これの仕様を確認しておきましょう。
初めに、strtok_setBuffer で、区切られる対象の文字列を選択してもらいます。その文字列は、strtok モジュールの中にある変数 stt_buf に保存しておきます。
次に、strtok_getnext() を呼び出します。これは名前の通り、「次の文字列を取り出す」ような関数です。引数がありませんが、必要な情報は既にモジュール内にあるので、大丈夫です。
ここでは、単純に、次の位置 (stt_index) から、次の区切り文字列を検索し、その手前までにある文字列を取り出します。
strtok_isFinished() は、文字列をすべて取り出し終えたかどうかを知るための関数です。この例のようなループの、終了条件に使います。
以上。かなり小規模なモジュールですね。
実際に使用するときは、これを strtok.as などの名前のファイルとして保存し、次のように書いておきます。
#include "strtok.as"
これで、先ほどのような命令、関数が使用できるようになります。
いちいち区切り処理を書かなくていいので、楽ですね!
つ getstr
さて、strtok のサンプルにある文字列データは、各行がCSV形式[脚注]になっています。実際はこれも「リンゴ」「100」「30」に分解したいはずです。
じゃあ、文字列をカンマで区切ればいいんですね。うーん、どう書こうか……。
strtokモジュールがあるじゃないか!
というわけで、次のように書いてみました。
#include "strtok.as" // 商品データ (という設定) // 「商品名,単価,在庫数」ということにする buf = {" リンゴ,100,30 みかん,170,12 ぶどう,1600,2 "} cntItem = 0 // 商品の種類数 // strtok バッファに設定 strtok_setBuffer buf, "\n" // すべての行について、1行ずつ処理 repeat if ( strtok_isFinished() ) { break } data = strtok_getnext() // data を分解 strtok_setBuffer data, "," name(cnt) = strtok_getnext() price(cnt) = int( strtok_getnext() ) stock(cnt) = int( strtok_getnext() ) cntItem ++ loop // 商品リストを再構成 repeat cntItem mes strf("「%s」は単価 %d 円で、%d 個の在庫があります。", name(cnt), price(cnt), stock(cnt)) loop stop
「リンゴ」は単価 100 円で、30 個の在庫があります。
実行結果
……。あれ? リンゴしかない……?
原因は簡単です。
りんごの行を分解するときに、strtok にりんごの行のテキストを設定しますが、それによって元の商品リストの文字列が消滅してしまったのです。
そのため、ループの2回目の時点で、strtok は「取り出しが完了した状態」になっています (設定されているのはりんごの行)。
というわけで、こういう処理は、一旦各行に分割してから、その各行について strtok を使う、という手順を踏まなければいけません。面倒くさい!
今回の例ではそうして解決できるのですが……
もし、複数のプログラムが strtok を利用していて、その片方が strtok を利用した処理を終える前に、もう片方が strtok に新しい文字列を設定してしまった場合。
プログラムは見た目とは違う挙動、すなわちバグを生じてしまいます。
今回も2本立てです。
実は、このページで言いたいことは、冒頭で述べた、「1つの #module と、1つの目的のための処理を行うユーザ定義命令・関数が #module の中にいくつか定義されているものを、(HSPでは) モジュールと呼ぶ」だけです。
その他は、すべて、次への布石なんです。今回が事件編、次回が解答編といったところ。
では、また次回。