【第49回】ロータス博士のWinActor塾~文字列分割 解答編

さて、今回は第48回でワシが出題した文字列分割を使ったシナリオについて
解答と解説をしていこうと思うぞ。
まだチェックしていない方は前回の記事を見てチャレンジしてみよう。
前回の記事はこちら。


言われた通り作ってきたんですけど・・・
正直うまくできてるか不安です。

短いシナリオじゃから作るだけなら容易じゃろう。
ただ、作り方となると何通りもあって奥が深いからのう。
今回はワシのシナリオを見て、自分で作ったところとの違いを見て欲しい。

あ、よかった。
悪い例とかで僕のシナリオが出されると思ってヒヤヒヤしてましたよ。
博士のシナリオ、参考にさせてもらいますよ!
解答例シナリオ
練習問題
文字列分割のライブラリを使用して次の文字列をエクセルに転記してください。
「Sun,Mon,Tue,Wed,Thu,Fri,Sat」
転記後のシートの状態は下図の通りになるようにシナリオを作成しましょう。


こんな感じの問題じゃったな。こういうのは正解を見たほうが早いからの。
早速ワシの解答例をダウンロードしてみてくれ。
その上で、どうしてこういった作りにしたのかをエリアごとに解説するぞ。
シナリオ解説
それでは、シナリオの中をみてみましょう。
まずはシナリオ解答例の全体像をチェックします。
フローはループがひとつだけのシンプルな作りになっていますね。
続いて、変数とその初期値を確認します。
変数は二つのグループに分かれています。
文字列分割ノードのプロパティに使用するものが「文字列操作」グループに、
Excelへの転記処理に必要な変数が「Excel関連」にそれぞれ作成されています。
それでは、上から順に処理を追っていきましょう。
事前準備
この部分では、目的の動作を行う前の準備を行っています。
今回のシナリオでの事前準備は「変数の初期化」と「エクセルファイルを開く」です。
『変数値設定』のプロパティを見てみましょう。
変数「インデックス」に0を設定しています。
これにより、毎回シナリオが実行されるたびにインデックスという変数が0になることを示します。

博士、なぜわざわざ変数値設定を使っているんですか?
普通に変数一覧の初期値に0を入れればいいのに。

その意見はもっともじゃな。
これは汎用性と見やすさの向上のためにあえてそうしているのじゃ。
汎用性については後で説明しよう。
見やすさについてじゃが、変数は実際にどのタイミングで使用されているかわかりにくいのじゃ。
じゃからこうして変数に値を入れたということをフロー上に示すことで、後からフローを見たとき
すぐに変数の値が更新される部分を知ることができる。
まあ、このあたりは作り手の好みにもよるがのう。

なるほど、ただ単にノードを減らせばいいわけではないんですね。
わざとノードにしてフローに置くことで、ぱっと見でわかるシナリオになると。
次に『Excel開く(前面化)』のプロパティです。
「転記用シートファイルパス」の変数に入っているパスのエクセルファイルを開きます。
今回のシナリオでは相対パスを使用しているため、そのまま実行できます。
また、転記用のエクセルファイルにはシートが1つしかないためシート名は空欄で構いません。
以上で事前準備は完了です。
ここまでで覚えておくべきポイントはインデックスを0にしたということです。
転記処理(ループ)
今回の転記処理にはループを使用しています。
繰り返しノードのプロパティは次のようになっており
無限ループとなっています。
文字列の分割と転記は、この無限ループ内で実行されます。
メインとなる転記処理部分を上から見ていきましょう。
『文字列分割』のプロパティは次のように設定されています。
全て変数が設定されていますね。
この時点での各変数の中身は次のようになっています。
このノードが実行されると「分割文字列」と「分割数」に結果が格納されます。
分割元文字列を「,」で分割したときの0番目の文字列が「分割文字列」に入り、
それと同時に、分割された文字列の数が分割数に入ります。
結果、ノードが実行された後の変数は次のようになります。
分割文字列に「Sun」が、分割数に「7」が取得できていますね。

元の文字列を分割して「Sun」が取り出せたな。
このまま転記といきたいところじゃが、ここで一旦ループについて考えてみよう。
ループについて考えるということは、1ループ毎の変数の変化を考えるということじゃ。
そうすることで、ループ内に必要な処理が見えてくるぞ。
まずは文字列分割しかループ内になかった場合を想像してみよう。

この状態でループがぐるぐる回るとしたら、変数の値はどうなっていくかのう。
分割元文字列は変化しないものとして一旦横に置いておいて、
そのほかのプロパティに設定した変数は次のようになる。

いやいや、博士なに遊んでるんですか。
何回ループしても同じに決まってるじゃないですか!

その通り!
今このループ内には文字列分割ノードしかないから変化しないのじゃ。
1回目はSunが取得できた。でも2回目はMonを取得したい。
どこを変えたら良いかわかるかのう?

簡単です!インデックスを1にすればいいんです!
インデックス0番目がSun、1番目がMonになりますからね!
3回目はTueが欲しいからインデックスを2にします!

うむ!正解じゃ!
では5回目までのループを表に反映させてみよう。
何かが見えてこんか?

さすがの僕でもここまでされたらピンときますよ!
インデックスが1ずつ増えていますね!

大正解!1ループ毎にインデックスを「+1」すれば良いことが一目瞭然じゃな。
ループ内でインデックスをカウントアップすれば良い訳じゃ。
実はこの部分がそれにあたるぞ。

ここのカウントアップはそれだったんですね。

これでループ毎に欲しい文字列が手に入る仕組みができた。
次はExcelへの転記じゃな。
転記もループのことを考えて、どのようにプロパティを設定したらよいか考えよう。
転記に必要な情報は、転記のノードのプロパティを見ればわかります。
まずは『Excel操作(値の設定2)』のプロパティを確認してみましょう。
必要な項目は「設定値」「ファイル名」「シート名」「セル(行)」「セル(列)」 の5つです。
「設定値」はループ毎に文字列分割によってSunやMonが格納される変数「分割文字列」を設定します。
「ファイル名」には初期値の入った変数「転記用シートファイルパス」を設定します。
「シート名」は、今回使用する転記用ファイルにシートが1つしかないため、空欄のままで問題ありません。
「セル(列)」はA列固定で良いため、初期値にAの入った変数「入力列」を用意して設定しましょう。
残るは「セル(行)」ですが、転記先は1行ずつずらす必要があるため少し工夫が必要です。

ハイ博士!今日の僕は冴えてますよ!
1ループ毎に転記する行が+1されてるから「セル(行)」にインデックスをあてればいいかも!

うーむ!おしい!
発想は素晴らしいが、ひとつ忘れておるのう。
インデックスは0から始まるのじゃ。
エクセルに0行目は存在しないからおかしなことになってしまう。

あ、そうか・・・しまった。
今のは忘れてください。

では「入力行」という変数で行をコントロールしよう。
これもわかりやすいように表にしてみると、次のような関係になる。
ロータス君の言ったやり方だと、インデックスをそのまま使うからこうじゃな。

本来は1行目から入力されて欲しいから、表は次のようになるべきじゃ。
この図を見ればやるべきことがわかってくるのではないかな?

これは・・・入力行はインデックス+1が正しいということですね。
1ループ目は0+1=1、2ループ目は1+1=2という感じで。
つまり必要なのは「入力行」の変数にインデックス+1を設定するノードです!

うむ!その通り!今日はまるで別人じゃな!
せっかくじゃから今回はその「+1」も変数にするぞ。「入力開始行」という変数がそれじゃな。
この変数には初期値として1を設定しておる。
そしてその加算処理を行っているのがここじゃ。
「入力開始行」は初期値として1が設定されているため、実質「1+インデックス」となります。
これで「入力行」に1ループ目には1、2ループ目には2が入るようになりました。
ここまでで、転記に必要な項目は全て揃いましたね。
あとは『Excel操作(値の設定2)』のプロパティに設定するだけです。
設定後のプロパティは次のようになります。
ループの終了
転記処理が出来上がったところで、ループから抜ける条件を設定しましょう。
今回のループは無限ループなので、必要な分だけ転記を終えたらループから抜けるようにしておく必要があります。
ループの最後に判定を行って抜けるかどうかの分岐を作りましょう。
ここでの”必要な分だけ転記”というのは、分割した文字列が全て転記されることを指します。
今回の文字列は7つに分割されるため、7回転記を行った時点で終了するようにします。
このシナリオで8回目の転記を行おうとするとエラーとなります。
文字列分割ノードで指定するインデックスには、存在しない数を入れてはいけないからです。
7つに分割される文字列での有効なインデックス範囲は当然「0~6」ですので
インデックスが7になってしまう8ループ目はエラーとなり停止してしまうのです。
これを逆に利用して、例外処理にループを入れるという技もありますが
想定外のエラーでループを抜けたときの検知が難しくなるためオススメしません。
正攻法でループから抜けましょう。

ここで設定している条件は次の図の通りじゃ。
なぜこのような条件なのかはまた表で解説しよう。
分割数は、文字列がいくつに分割できたかを示す数でした。
これは分割元の文字列や区切文字列を変えない限り、何度実行しても変化しません。
変化しない「分割数」は、ループの上限として利用できます。

待ってください!上のほうで見た表はこんな感じになってました。
インデックスは0から始まるからインデックス=分割数の条件がtrueになるのは
8ループ目になってしまうのでは?


ほう、面白いところに気が付いたのう。しかし心配は無用じゃ。
この図は転記を行った時点での変数の値じゃ。もう一度よくフローを見てみるのじゃ。
転記後にカウントアップされてるじゃろ?
つまり分岐が行われる時点ではインデックスは+1されておるのじゃ。
新しく表にするとこうじゃな。


あ、本当だ!確かにそうですね!
カウントアップのタイミングを考えてませんでした。
7回目で7=7、これなら問題ないですね。

うむ。そしてtrueのほうに「繰り返し終了」のノードを置いて
ループから抜けるようにすれば完了じゃ!
後片付け
ダウンロードできるシナリオ内には終了処理のグループが無限ループ後に配置されています。
実行後にエクセルファイルの内容を確認しやすいように空の状態となっていますが
任意でエクセルを閉じるなどの処理を追加してシナリオを完成させてください。

これでシナリオの解説は終了じゃ。
文字列操作とループの組み合わせについて理解が深まったかな?
ループ処理を作成する際の考え方の参考にもなるかと思うぞ。

こんなに短いシナリオなのに考えることが多かった気がしますね・・・
こういうシナリオがサクサク作れるようになるといいですね!
ところで冒頭で言ってた汎用性は・・・?

おっと、そうじゃった!
なぜこのような作りにしたか、どうしてこの作りだと汎用性が高まるのか、じゃな。
では最後にその部分を解説して終わりにしよう。
シナリオの汎用性を高めるために
変数値設定ノードの利用
ロータス君は今回のシナリオ内で使用するインデックスについて、変数の初期値として0を入れず
変数値設定ノードを使っていることに疑問を抱いていました。

もちろん初期値で設定するのは間違いではない。
しかし、もうワンランク上のシナリオを目指すなら、変数値設定ノードにしたほうが良いのじゃ。
例えば転記処理を2回行われるように変えてくれと言ったらどうするかのう?

うーん、まあ一旦全体をコピー&ペーストして作りますかね。

ではコピー&ペーストを想定して、サブルーチンにしてみよう。
繰り返す処理はサブルーチン化するのがベストじゃからな。

このようにサブルーチンにして2回呼び出せば2回実行したことになる。
この解答例であれば、そのまま全部サブルーチンにすることができる。
しかし変数値設定ではなく、変数の初期値でインデックスを0にしていると・・・


あ・・・

そうじゃ。
2回目の呼び出しでインデックスが0になっていないためにエラーとなったのじゃ。
インデックスを変数値設定にすることで一気にコピペが可能なシナリオになっていたのじゃ。

なんてことだ、ここまで考えて作られていたなんて・・・。
僕はまだまだ未熟ですね・・・。

繰り返し使える処理というのは、それだけで汎用性が高いのじゃ。
例えばサブルーチンの間で列を変更すれば、こうじゃ。



うわ、何の苦労もなく2列転記に変更できてる・・・。
これが汎用性の威力!

最初からこういったシナリオを作るのは難しいと思うが
作っていくうちに直感でわかるようになってくるぞ。
無限ループの使用
ロータス君にはもうひとつ疑問があるようです。
今回のシナリオなら、特に無限ループを使う必要もなくループ回数を7にしてしまえばよいのではという考えです。

これもちょっと気になってたんですよね。
ループ回数を決めてしまえば、毎回判定をする必要も無くてノード数も減るんじゃないかって。

ふむ、その考えはわからなくもないが、そうして作った処理は
そのシナリオ限定の処理になってしまうじゃろう。
ちょっと手間とノード数が掛かるが、今回のように無限ループにすれば変化に柔軟に対応できる。
試しに分割元文字列をいじってみよう。


実行結果はこの通りじゃ。


ただ分割元文字列の初期値を変えただけじゃがしっかり転記できておる。
シナリオは一切修正していないのじゃ。
ループ回数を固定してしまったらこうはいかん。

うわうわ、ここまでくるともう驚かないですね。
もしかして博士って結構すごいのでは?

今までなんだと思っておったんじゃ・・・。仮にも博士を名乗っておるんじゃが。
まあ、とにかくこのように変化に強いシナリオを作ると
使いまわしも効くし、修正も容易になるしと良いことが多いのじゃ。
これを機に、なんとなく作っていた処理も少し立ち止まって考えてみるとよいぞ。

今回のシナリオ簡単そうだったので正直ちょっとなめてました!
シナリオ作成って本当に奥が深いですね・・・。

今回のシナリオでは、変な値が入っていたときの例外処理などは省いておる。
さらに完璧なシナリオを目指す人は例外処理などを組み込んで
エラーにも強いシナリオにしてみよう!
関連記事こちらの記事も合わせてどうぞ。
2024.11.05
【第105回】ロータス博士のWinActor塾~新しい画像マッチング画面
2024.09.27
【第104回】ロータス博士のWinActor塾~ガイド利用シナリオ(作成編)
2024.09.10