第15回 いつも心にカーソルを「4.高度な繰り返し」
|
|
15−4 高度な繰り返し
ここで、ちょっと妙な考えを起こしてみましょう。 Do〜Loopでは簡単に作ることができた無限ループを、For〜Nextを使ってみる事にします。 そもそも、For〜Nextは、カウンタが最終値に到達すればループを抜けますので、それを逆手にとって最終値に達しないようにすれば無限ループになりますね。 まずは、1回ループするごとに最終値を増やしてみることにします。
なぜなら、Do〜LoopとFor〜Nextでは、そのループの方法が若干異なるからです。 For〜Nextの繰り返しは、具体的には次のような仕組みになっています。
したがって、どうしても無限ループにしたい場合、最終値を変更するのではなく、カウンタの方を変更しなければなりません。
通常は、わざわざこんなことをする人もいないでしょうが、プログラムを組んでいたら『結果的になってしまう』可能性は十分あります。こういう無限ループにならないよう、注意するに越したことはありません。 ちなみに、Do〜LoopにはFor〜Nextのような明示的なカウンタが存在しません。そのため、必ずWhileもしくはUntilの条件式をキチンと評価して判定しますので、ループ内で条件が変わっても、それに対応した判定が行われます。 ある意味、こちらも『結果的に』無限ループに陥ってしまう可能性を持っているわけです。 どちらも気をつけましょう。 ところで、先ほどのフローチャートをよく眺めてみると、変だなぁ、と感じる部分が1ヶ所あります。 1回ループをした後、ループを続けるか否かをどこで判定しているんだろう?という『具体的な場所』です。 いや、本来はどうでもいいことなんですが、ここでは、ちょっとこだわってみましょう。
つまり、ループの1回目が終わり、Nextから前に戻る『場所』がどこなのか?という点です。 Forの行は、初期値と最終値を決める行なので、Nextから戻るのは、その『次の行』、すなわち For〜Nextとは無関係な『普通のコマンド』がかかれている行です。 そうすると、実際にカウンタの加算(減算)と条件判定を「Next」が行っていると考えるのが自然ですね。
前項で登場した1回もループしないループ(?)ですが、もし、先ほどの図のように「Next」がカウンタ処理と条件判定を行っているならば、最低1回は「In」が表示されるはずです。
とすれば、このフローのように、ループに入る前のForの段階と、Nextの2ヶ所で、ループのチェックを行っていると考えても良さそうです。 しかし、何処かしらスマートさに欠けますよね。 ここで、存在を忘れかけていた「To」に注目してみましょう。 実は「To」が条件判定、「Next」がカウンタの処理をしていると考えれば、かなりスッキリします。
特に、全体図もそうですが「今、どこを処理しているの?」という具体的な場所をイメージするには最適な道具です。 とは言え、これほどFor〜Nextにこだわった解説は不要ですよね。一般の解説書のように「Nextでは、Forの次の行に戻ります」としておく程度で十分です。 さて、脱線しましたが、元に戻りましょう。 ループといっても、実際のプログラミングにおいては、直線的に1つのループを実行するだけではありません。 場合によっては2重、3重のループを扱う場合があります。 例えば、画面に掛け算の「九九」を表示することを考えてみましょう。 ご存知のとおり、九九は1から9を掛け合わせた81通りの数値の羅列で、一覧表にするならば、縦、横、それぞれ9つずつの数字が並ぶことになりますが、このように縦、横に展開する値を扱う場合、繰り返しを1重で考えるより、次のように2重に考えたほうが、はるかに効率よく処理することが可能です。
もし、1重のループだけで作ろうと思うと、結構面倒です。 ところでこのループの順番、プログラムをよく見ればわかるかと思いますが、外側のループが1つ進む間に、内側のループが1回完結します。 内側のループの変数Xは、描くべき文字列の横方向を指定していますから、プログラム的には、1行描いては下へ、また1行描いては下へ、を繰り返しています。 もちろん、XとYのループを入れ替えても動作しますが、入れ替えた場合は、1列描いては横へ、という動きに変わることは理解できると思います。
特に、実行の順番が動作上、何らかの問題になる場合、その結果に大きく影響します。 多重ループのように、内側、外側がキチンと決まっている構造を専門的には「ネスト(入れ子)構造」と言います。 そして、多重ループのネストが重なることを「深さ」や「段」という呼び方で呼ぶこともあります。 NS Basicの多重ループのネストは、最高10段までの深さが許可されています。
そのため、多重ループにも制約があるわけですが…ひとまず、10段もあれば大丈夫ですね。 さて、ネストについて「内側、外側がキチンと決まっている構造」と書きましたが、キチンと決まらない場合もあるんでしょうか? あります。ループが重なってしまう場合です。 例えば、
順に考えてみましょう。 まず、最初の、For〜Nextの処理は問題なく行われるはずです。途中にあるDo untilはネストが許す限りは関係ないですから。 一方、無事にFor〜Nextのループを抜けた後、Loopにたどり着きますが、これもDoに戻るでしょう。しかし、その次に登場するNext、これには戻るべきForがありません。 こういう場合、コンパイル時にエラーになるか、実行時にエラーになります。 まぁ、ループのだけの処理で交差するような構造を書いてしまうことは少ないでしょうが、このネストというのは分岐のコマンドでも重要な意味を持っています。
旧来のBASICでは、Nextの後に、どの変数をループさせているのか省略できなかったので、このようなループの交差について、比較的大きな解説ポイントとして取り上げられていました。
その一方、ある分野では「ネスト」の話題をよく見かけます。 それは、構造化言語XMLのタグです。 入門的な解説書などでは、同じようなタグのHTMLが結構省略可能だったのに対して、XMLはキチンとネストを守らなければならないよ、と比較して説明されていたりします。 元々、プログラミングなどでキチンとネストを守ってきた人たちから見れば、むしろ自然でしょうが、省略OKだったHTML派などから見れば、面倒だなぁ、と思うことでしょう。 どちらにしろ、頭の固いコンピュータには、人間の都合で省略するよりも、キッチリ指示する方が効率が良いのかな、などとも思ってしまいます。 えっと、話が外れてしまいましたが、多重ループが交差することによって発生するエラー、それは繰り返しだけではなく、分岐などあちらこちらで発生する可能性を持っています。 プログラムコードの先頭を、ネストごとに揃えるなどして、複雑な処理中でも、間違いを最小限に押さえるような工夫を心がけましょう。 |
||||||||||||||||||||
第15回 いつも心にカーソルを「4.高度な繰り返し」
|