
初級から中級へ:BREAK文とCONTINUE文
はじめに
ここで提示されるコンテンツは、教育目的のみを目的としています。いかなる状況においても、提示された概念を学習し習得する以外の目的でアプリケーションを閲覧することは避けてください。
前回の記事「初級から中級まで:WHILE文とDO WHILE文」では、ループ構造について紹介しました。そのため、今回の記事を十分に理解するには、前回の記事で解説した内容や例題を理解していることが前提となります。
多くの初心者プログラマーが、ループ生成コマンドの仕組みを正しく把握するのに苦労していることはよくあります。しかし、ループはプログラミングにおいて不可欠な要素であり、趣味でプログラムを作成する場合であっても、ほぼすべてのプロジェクトで登場します。したがって、ループ関連のコマンドを正しく理解し、効果的に使いこなせるようになることは非常に重要です。
なお、従来のコマンドを使わない別タイプのループも存在しますが、それについては別の機会に取り上げます。今回は、ループ内で頻繁に使用される3つのコマンドに焦点を当てます。そのうち2つはループ外でも使用できますが、1つはループ内部でのみ使われます。ただし、特別な理由がない限り、単独での使用は避けるべきです。
本記事では、これら3つのコマンド、とくにタイトルにもある2つのコマンドに注目して解説していきます。つまり、ループを内部から制御する方法を学ぶ時が来たのです。ここから先の説明には、ぜひ注意深く目を通してください。もしこれらの概念を正しく理解できなければ、プログラミング学習のどこかで必ず壁にぶつかることになるでしょう。
考えられる答え
始める前に、前回の記事で小さな課題を出していたのを覚えていますか。その課題とは、次のコードをループを使ったバージョンに書き換えるというものでした。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Factorial(); 07. Factorial(); 08. Factorial(); 09. Factorial(); 10. Factorial(); 11. } 12. //+------------------------------------------------------------------+ 13. void Factorial(void) 14. { 15. static uchar counter = 0; 16. static ulong value = 1; 17. 18. Print("Factorial of ", counter, " => ", value); 19. counter = counter + 1; 20. value = value * counter; 21. } 22. //+------------------------------------------------------------------+
コード01
もし解答を思いつかなかったとしても、心配する必要はありません。この段階では、それも学習プロセスの一部です。ただし、学習する前に内容を溜め込んでしまわないように注意してください。これから中級レベルへ進むにつれて、概念や知識がどんどん積み重なっていきます。
コード01にループを取り入れて(あるいは再構成して)効率を高める方法はいくつか存在します。その中の一つは、前回の記事で扱った内容を応用することで、すでに実現可能になっています。
簡単に言えば、前回紹介したコード07を少し変更することで、コード01と同じ結果を得ることができます。どうするかというと、以下に示すコード02を使用するのです。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. uchar counter = 0; 07. 08. while (counter < 5) 09. Factorial(counter); 10. } 11. //+------------------------------------------------------------------+ 12. void Factorial(uchar &counter) 13. { 14. static ulong value = 1; 15. 16. Print("Factorial of ", counter, " => ", value); 17. counter = counter + 1; 18. value = value * counter; 19. } 20. //+------------------------------------------------------------------+
コード02
これはあくまで一つの解答例にすぎません。ほかにもさまざまなアプローチがあり、より複雑なものもあれば、もっとシンプルなものもあります。ですが、これまでに学んできた範囲を踏まえると、この方法が堅実な選択だと言えるでしょう。さて、もしコード02が「何をしているのか」あるいは「なぜコード01と同じ結果が得られるのか」をきちんと理解していない場合は、この記事を読み進めるのを一度ストップしてください。必ず前回の記事に戻り、コード02の仕組みを完全に理解してから、この記事や今後の記事を読み進めるようにしましょう。
この注意を伝えたところで、いよいよこの記事の最初の文法事項を見ていきましょう。まずは、ループ内で使える最もシンプルな文から始めます。ただ、その前に、新しいトピックを一つ紹介しておきます。
RETURN文
MQL5プログラミングにある程度慣れている方なら、この文はループとは関係なく、むしろ関数に関連しているはずだと思われるかもしれません。その通りです。RETURN文はループよりも関数との関連性がはるかに強いものです。しかしながら、関数においてはRETURN文は必須ですが、プロシージャでは任意となります。プロシージャ内でのRETURN文の効果によって、時にはループ内にRETURN文が登場することがあるのです。
さて、ここからは特に注意して読んでください。RETURN文の役割は、それを呼び出した関数またはプロシージャに制御を返すことです。ただし(そしてここが多くの初心者がつまずくポイントなのですが)、もしアプリケーションのメインプロシージャ(たとえばOnInitやOnStartなど)の中にRETURN文を記述した場合、RETURNが実行されるとアプリケーション自体が終了してしまいます。その結果、MetaTrader 5はチャートからアプリケーションを自動的に取り除きます。これは、すべてのアプリケーションが本質的にはオペレーティングシステムによって実行される「関数」または「プロシージャ」であるためです。この仕組みを理解すれば、OnInitやOnStart内にRETURN文を書くとアプリケーションが途中で終了する理由も納得しやすくなるでしょう。
なお、OnInit内でRETURNが使われた場合、アプリケーションが終了するかどうかは、MetaTrader 5に返される値によって異なります。一方で、OnStartプロシージャ内でRETURNが実行されると、アプリケーションは確実に終了します。
この動作を理解するために、まずは簡単な例から見ていきましょう。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 07. 08. return; 09. 10. Print(__FUNCTION__, " ", __LINE__, " This line will never be executed..."); 11. } 12. //+------------------------------------------------------------------+
コード03
このコードを実行した結果は以下のようになります。
図01
ここで理解しておくべき重要なポイントは、8行目にあるRETURN文がプロシージャ内に存在しているということです。プロシージャの場合、RETURNに戻り値を関連付けることはできません。これは、実行ファイルを生成する際にコンパイラとの競合エラーを防ぐためです。一方、もしRETURNが関数内にある場合は、必ず戻り値を関連付けなければなりません。なぜなら、呼び出し元の関数は戻り値を受け取ることを前提としているからです。この点については、以前「変数」について解説した際にも触れましたね。
さて、とはいえ今回のテーマはそこではありません。今注目すべきは、「ループを途中で終了させる方法」です。特定の条件が発生した場合、ループを早期に抜ける必要があり、むしろそのほうが望ましいことも多いです。この考え方を適用できる簡単な例を、以下に示します。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 07. 08. Procedure_Loop(); 09. 10. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 11. } 12. //+------------------------------------------------------------------+ 13. void Procedure_Loop(void) 14. { 15. char info = 10; 16. 17. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 18. 19. while (info) 20. { 21. info = info - 3; 22. 23. Print("Loop : ", info); 24. 25. if (info < -5) return; 26. } 27. 28. Print(__FUNCTION__, " ", __LINE__, " This line will never be executed..."); 29. } 30. //+------------------------------------------------------------------+
コード04
コード04を実行すると、下の画像に示すような結果が生成されます。
図02
この時点で、動作に少し違和感を覚えるかもしれません。もしinfo変数が-5未満になったときにループを終了させたいのであれば、その条件をループのテスト式に直接組み込めばよいのではと思うかもしれません。しかし、そうしない理由は、必ずしもその値に到達することを前提としていない場合があるからです。ただし、もしその値に到達してしまった場合には、ループが存在するルーチンまたは手続きの残りの処理を無視したいケースがあるのです。
この仕様により、コード04では28行目が実行されることはありません。なぜなら、infoの値がゼロに到達する前に、IF文によってRETURN文が実行されるからです。その結果、画像02では7から-8までの値が表示されているのが確認できます。
ここで、なぜ25行目が実行されてもアプリケーションは終了せず、10行目が実行できるのだろうと疑問を持つかもしれません。これはコード03で見た挙動とは異なります。読者の皆さん。理由は、OnStartはMetaTrader 5によって直接呼び出される初期プロシージャ(または関数)であり、Procedure_LoopはOnStartから呼び出されているということです。つまり、RETURN文が実行されると、制御はProcedure_Loopを呼び出した関数(この場合はOnStart)に戻るのです。
MetaTrader 5がどのように関数呼び出しを管理しているかについては、後ほど詳しく説明します。ここでは簡単に、MetaTrader 5は主に2つの関数(OnStartとOnInit)を呼び出すことだけ押さえておきましょう。この2つの特別なケースにおいてのみ、RETURN文はアプリケーションの終了を引き起こします。それ以外の場合、RETURNを使ってもアプリケーションは終了しません。
素晴らしいですね。ここまでで、ループ内でRETURNを使う方法についてしっかり理解できたはずです。もしまだ疑問点があれば、ここで紹介した簡単な例を使って、納得できるまで練習してみてください。それでは次に、この記事で取り上げる次の文法について見ていきましょう。
BREAK文
BREAK文はとても興味深いものですが、慎重に扱う必要があります。この文については、焦らずじっくり学習していきましょう。初心者が使い方に戸惑うのは、決して珍しいことではありません。単純なループ内でBREAKを使うのは比較的簡単ですが、より複雑なループ構造になると、状況はぐっと難しくなる可能性があります。
BREAKを使用する際によくある間違いのひとつは、「他のループ」や「別の制御フロー文」にも影響を与えると誤解してしまうことです。これは、BREAKがループ専用ではなく、後ほど説明する「別の制御構造」にも使われるためです。とはいえ、今の時点ではそのことは気にしなくて大丈夫です。まずはループ内でのBREAKの動きに集中して学んでいきましょう。
ここで、多くのプログラマーが見落としたり忘れがちな、重要な考え方があります。それは、BREAK文はRETURN文と非常によく似た原則に従うということです。つまり、ループの中に無造作に置くのではなく、通常はIF文と一緒に使うということです。これをわかりやすくするために、前のセクションで紹介したコード04を使って説明しましょう。そこではRETURN文を使っていましたが、今回はそれをBREAK文に置き換えたバージョンを示します。以下がその更新版コードです。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 07. 08. Procedure_Loop(); 09. 10. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 11. } 12. //+------------------------------------------------------------------+ 13. void Procedure_Loop(void) 14. { 15. char info = 10; 16. 17. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 18. 19. while (info) 20. { 21. info = info - 3; 22. 23. Print("Loop : ", info); 24. 25. if (info < -5) break; 26. } 27. 28. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 29. } 30. //+------------------------------------------------------------------+
コード05
コード05を実行すると、MetaTrader 5端末に表示される出力は次のようになります。
図03
面白いですね。今回はこれまで触れなかった情報が登場しました。本当に興味深い内容です。ループの操作がこんなに簡単にできるとは、驚きです。これで、もっと複雑なコードにも対応できる準備が整いました。(笑)さて、落ち着いてください、親愛なる読者の皆さん。実は、まだ始まったばかりです。これらの文について、初心者のプログラマーがよく直面する問題(実際には誤解)があります。ですので、ここで紹介したコードのバリエーションを使って、しっかり練習してみてください。
最初の誤解は、ネストされたループでのBREAK演算子の使用に関するものです。そう、複数のIF文をネストできるように、複数のループを入れ子にして使うこともできます。これは、多次元行列を扱う際に非常に一般的です。インデックスを手動で計算せずに済むので、とても便利です。さて、この場合、BREAK文がどのように動作するのかを理解するために、ネストされたループの簡単な例を見てみましょう。以下にその例を示します。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 07. 08. Procedure_Loop(); 09. 10. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 11. } 12. //+------------------------------------------------------------------+ 13. void Procedure_Loop(void) 14. { 15. char info = 10, 16. limit = 5; 17. 18. do 19. { 20. Print(__FUNCTION__, " ", __LINE__, " : Info (", info, ") Limit (", limit, ")"); 21. 22. while (info > limit) 23. { 24. if (limit < 0) break; 25. 26. info = info - 3; 27. 28. Print(__FUNCTION__, " ", __LINE__, " : Info (", info, ") Limit (", limit, ")"); 29. } 30. info = info + limit; 31. limit = limit - 2; 32. 33. Print(__FUNCTION__, " ", __LINE__, " : Info (", info, ") Limit (", limit, ")"); 34. 35. }while (info > 0); 36. 37. Print(__FUNCTION__, " ", __LINE__, " : Info (", info, ") Limit (", limit, ")"); 38. } 39. //+------------------------------------------------------------------+
コード06
最初は表示されるメッセージの数が多いため、コード06は複雑に見えるかもしれませんが、実際には非常にシンプルです。コード06は、ネストされたループ内でBREAK文がどのように動作するかを効果的に示しています。実行すると、その結果は非常に興味深いものになります。以下に示します。
図04
さて、読者の皆さん、よく注意してください。18行目から始まるループ内には、22行目から始まる別のネストされたループがあります。このネストされたループ内では、24行目で条件がチェックされます。ここが面白いポイントです。後で、BREAK文をRETURN文に置き換えて、コードがどのように動作するかを実験してみることもできますが、今はBREAKを使用した場合に何が起こるかに焦点を当てましょう。
ここでは何らかの集計が行われていることが明らかですが、それはどのように制御されているのでしょうか。それが重要な質問です。多くの場合、特定の値を追跡する必要がありますが、複数のネストされたループの出力を手動で分析するのは手間がかかります。そこで、計算方法を定義する数式や公式を作成します。この数式ができたら、それをコードの形で実装します。
その際、数式を関数コードに変換する能力を信じる必要があります。処理する値の数やその生成方法が特に複雑で広範囲にわたる場合、個別に各値を分析することはできません。代わりに、サンプリングを使用して、定義された範囲内で期待される値を決定し、その範囲のみを分析します。このため、各コマンドを習得し、すべての操作がどのように機能するかを理解することが重要です。
コードに戻ると、図04では、22行目のループの条件がより頻繁に実行されるべきだと示唆されているにもかかわらず、28行目が特定のケースでのみ表示されていることに気付くでしょう。これは、BREAK文が内部ループを終了させ、条件が終了を示す前にそのループを終了させるためです。ただし、この特定のケースでは、内部ループを中断しても外側のループは終了しません。これが、BREAKをRETURNに置き換えて、ターミナルの最終出力にどのように影響を与えるかを確認する理由です。結果に明確な違いがあります。
実行フローを追っていけば、この概念を簡単に理解できるはずです。以前の記事で、WHILEループやDO-WHILEループ、IF文の実行フローをすでに説明しているので、この文脈でもそれらを視覚化できるはずです。しかし、BREAK文に遭遇したときの実行フローについてはまだ説明していません。これを以下に図示します。
図05
ここで、LOOPはループを作成するための任意のコマンドを表します。しかし、BREAK文は、まるでコード内に自由に配置されているかのように示されています。この時点で、1つ明確にしておくべきことがあります。BREAK文が正しく実行されるために必要なすべての条件を含める場合、ここでIF文の実行フローを追加する必要があります。IF内の条件がtrueと評価されると、BREAK文が実行され、フローは画像に示された通りのパスに従います。また、BREAK文はルーチンの実行前、実行中、または実行後に配置できますが、通常は先頭か末尾に配置されます。もし真ん中に現れる場合、それはルーチンが2つの部分に分かれていることを意味することが多いです。しかし、実行フローをシンプルに保つため、このタイプの操作は図には含めていません。
この時点で、概念は理解できていると思います。ただし、1つ重要な点を覚えておいてください。BREAK文は、別の種類の制御フローコマンドにも関連しています。今はその議論を後回しにして、一歩一歩進んでいくことにします。
次に、ループ内に現れる最後の文について見てみましょう。しかし、これまでに説明したコマンドとは異なり、この次に紹介する文はループ内にのみ存在し、使用時には特別な注意が必要です。
CONTINUE文
CONTINUE文は、特にWHILEループやDO-WHILEループ内で使用する際に本質的に危険です。これは、この文を使用することで誤って無限ループが発生することが非常に多いためです。後で説明するFORループは無限ループになりにくいように見えるかもしれませんが、それでも発生する可能性があります。通常、これは実装中の単純な見落としが原因で、CONTINUEコマンドの使用が必要となる場合です。したがって、コードのどの部分でこの文を使用する必要がある場合や、遭遇する場合は、常に注意深く、慎重に扱ってください。この文は常に何らかの形式のループに関連していますが、特にWHILEループやDO-WHILEループを扱う際には、特別な注意が必要です。
この文の誤用による潜在的な危険性を考慮し、ここでは少し異なるアプローチを取ります。まず、この文に遭遇したときの実行フローを理解することから始めます。それについては、以下の画像を参照してください。
図06
CONTINUE文に関しても、以前のBREAKやRETURNと同様のアドバイスが当てはまります。ただし、この場合、IF文の内部でBREAKやRETURNをルーチンとして使用するアドバイスは、さらに重要です。画像06に示されているように、CONTINUEを使用すると、ループルーチン内では何も実行されません。他のループを終了させる文とは異なり、CONTINUEは制御フローを即座にループの先頭に戻します。
したがって、ループ式がfalseと評価される条件が整っていない場合、ループは必然的に無限に実行され続けます。このため、CONTINUE文を使用する際には非常に注意が必要です。多くのプログラミング言語では、このような問題が発生する可能性があるため、GOTO文は実装されていないか、使用されていません。ただし、正しいコンテキストで適切に使用すれば、CONTINUEは多くの状況で非常に有用です。しかし、使用する際は常に冷静に、細心の注意を払ってください。
CONTINUE文を使った不注意な実装が引き起こすリスクを示すために、外部のイベントによってのみ終了するコードを作成しましょう。これには、次のコードを使用します。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 07. 08. Procedure_Loop(); 09. 10. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 11. } 12. //+------------------------------------------------------------------+ 13. void Procedure_Loop(void) 14. { 15. char info = 10; 16. 17. Print(__FUNCTION__, " ", __LINE__, " : Info (", info, ")"); 18. 19. while ((info) && (!TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) && (!IsStopped())) 20. { 21. if (info) continue; 22. 23. info = info - 3; 24. 25. Print(__FUNCTION__, " ", __LINE__, " : Info (", info, ")"); 26. 27. if (info < -5) return; 28. } 29. Print(__FUNCTION__, " ", __LINE__, " : Info (", info, ")"); 30. } 31. //+------------------------------------------------------------------+
コード07
コード07で何が起こっているかを説明する前に、まずそれを実行した結果を見てみましょう。このコードは、イベントの発生を待機している間に無限ループに入るため、静止画像は使用しません。代わりに、アニメーションを使用して、アプリケーションを終了する方法を示します。以下のアニメーションをご覧いただけます。
アニメーション01
さて、読者の皆さん、次のことを考えてみてください。これは、作成している無限ループを終了する方法を実装したためにのみ発生しました。この実装がなければ、MetaTrader 5からアプリケーションが強制的に削除されるまで、無期限にその状態が続くことになります。その他の状況ではアプリケーションは終了しません。この警告と注意を考慮して、コード07で何が起こっているのかを詳しく見てみましょう。
このコードはこの記事で見てきた他の例のバリエーションなので、本当に重要な部分に焦点を当てることができます。最初に注目すべき点は、ループが始まる19行目に示されているように、15行目の変数はWHILEループの実行を許可するように宣言されていることです。次の重要な点は、ループを終了するための条件が3つあることです。1つ目は、info変数がfalseになったときです。2番目は、ESCキーが押されたときです。3番目は、スクリプトの終了を要求するときです。3番目の条件がない場合、MetaTrader5は成功するまでスクリプトを閉じようとします。このような状況が発生すると、下の画像に示すように、端末に異なるメッセージが表示されます。
図07
これは、コードを終了する最悪のシナリオを表します。ただし、図07のこのメッセージは、3番目の条件(コード07の19行目)が欠如している場合にのみ表示されることを再度強調することが重要です。この条件が存在すると、結果はアニメーションで観察されたものと同じになり、コードが適切に終了したという印象を与えます。
しかし、ここで重要な点は21行目です。ループ内で何かを変更する必要があるかどうかを確認するIF文が存在しているにもかかわらず、IF文内のルーチンがCONTINUE文のみで構成されているため、ループ全体が無限サイクルに強制される点に注意してください。これは、WHILEループ内の他の部分が実行されないためです。
ただし、同じコードに小さな変更を加えることで、リスクを軽減することができます。23行目から27行目は依然として実行されませんが、その例として、次に示すように、コード07をコード08のように変更することが挙げられます。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 07. 08. Procedure_Loop(); 09. 10. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 11. } 12. //+------------------------------------------------------------------+ 13. void Procedure_Loop(void) 14. { 15. char info = 10; 16. 17. Print(__FUNCTION__, " ", __LINE__, " : Info (", info, ")"); 18. 19. while (((info = info - 3) > 0) && (!TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) && (!IsStopped())) 20. { 21. if (info) continue; 22. 23. info = info - 3; 24. 25. Print(__FUNCTION__, " ", __LINE__, " : Info (", info, ")"); 26. 27. if (info < -5) return; 28. } 29. Print(__FUNCTION__, " ", __LINE__, " : Info (", info, ")"); 30. } 31. //+------------------------------------------------------------------+
コード08
コード08を実行すると、アニメーション01に表示されたものとは異なる出力が端末に表示されます。以下をご覧ください。
図08
ご覧のとおり、変数infoには異なる値が設定されています。読者の皆さん、これはコードの小さな変更によるものです。具体的には、19行目のWHILEループ式の最初の条件部分です。この特定のケースでは、無限ループは作成されていないことに注意してください。ただし、これによってループルーチンの残りの部分が実行可能になるわけではありません。これは、Procedure_Loopが本質的には以下のフラグメントに示すように要約できるためです。
. . . 12//+------------------------------------------------------------------+ 13void Procedure_Loop(void) 14{ 15 char info = 10; 16 17 Print(__FUNCTION__, " ", __LINE__, " : Info (", info, ")"); 18 19 while (((info = info - 3) > 0) && (!TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) && (!IsStopped())); 20 21 Print(__FUNCTION__, " 29 : Info (", info, ")"); 22} 23//+------------------------------------------------------------------+
フラグメント01
これは、IF文内の21行目にあるCONTINUEコマンドによって、ループ内で毎回実行されるためです。これは非常に興味深いものでした。しかし、もし何らかの理由で、WHILEループの代わりにDO WHILEループを使用していたら、どうなっていたでしょうか。さて、親愛なる読者の皆さん、間違いなく多くの人の興味を引く、とても面白いシナリオが生まれていたでしょう。コードの実装方法によっては、非常に複雑な動作を引き起こす可能性もあります。
そこで、DO WHILEループでCONTINUEコマンドを使用するとどうなるかを示すために、非常にシンプルなバージョンで実験してみましょう。この例は以下のコード09に示されています。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 07. 08. Procedure_Loop(); 09. 10. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 11. } 12. //+------------------------------------------------------------------+ 13. void Procedure_Loop(void) 14. { 15. char info = 10; 16. ulong counter = 0; 17. 18. Print(__FUNCTION__, " ", __LINE__, " : Info (", info, ")"); 19. 20. do 21. { 22. Sleep(500); 23. 24. Print(__FUNCTION__, " ", __LINE__, " : Counter (", counter = counter + 1, ")"); 25. 26. if (info) continue; 27. 28. Print(__FUNCTION__, " ", __LINE__, " : Info (", info, ")"); 29. 30. }while ((info = info - 2) > 0); 31. 32. Print(__FUNCTION__, " ", __LINE__, " : Info (", info, ")"); 33. } 34. //+------------------------------------------------------------------+
コード09
最初は、ここで無限ループが作成されていると思うかもしれませんが、このセクションの前のコード例で示した小さなポイントにより、それは発生しません。ただし、DO WHILEループの使用経験が浅いプログラマーの多くは、26行目が実行されるとループが20行目に戻ると考えてしまうかもしれません。このような考え方は、DO WHILEループにおける実行フローの仕組みを理解していないことに起因します。実行フローについては前回の記事で説明しましたので、ここでは詳しく触れません。しかし、このコードを検証すれば、ひとつの事実が確認できます。それは、コード09に示されているように、28行目は絶対に実行されないということです。これは26行目の存在と、DO WHILEループ内での実行フローの特性によるものです。
これを説明するために、静止画像ではなくアニメーションを使うことにしました。私の考えでは、この方法の方がはるかに効果的であり、コード09がなぜそのように動作するのかを考え始めるきっかけにもなります。以下のアニメーションをよく観察し、それがどのように、そしてなぜそうなるのかを理解してみてください。
アニメーション02
最終的な考察
この記事では、ループ内でのRETURN、BREAK、CONTINUE文の使用方法について、できる限りシンプルかつ明確に説明するよう努めました。内容の多くはWHILEループとDO WHILEループの使用に焦点を当てましたが、ここで解説した原則は、後ほど説明するFORループにも適用されます。
ただし、親愛なる読者の皆さんには、この記事で取り上げた内容をしっかりと学び、実践するための時間を取っていただきたいと思います。プログラミングのより高度なレベルに進むにつれ、これらの概念を完全に理解していることが求められるようになります。なお、ここで扱ったトピックや概念は、以降のより複雑な内容では再度取り上げられません。
したがって、添付ファイルにあるコード例を最大限に活用してください。これら3つのコマンド(RETURN、BREAK、CONTINUE)が、アプリケーション実行時のコードフローにどのような影響を与えるかに注意を払いながら、じっくりと学習してください。それでは、次回の記事でお会いしましょう。お楽しみに。
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/15376





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索