English Русский Español Deutsch Português
preview
初級から中級まで:配列(II)

初級から中級まで:配列(II)

MetaTrader 5 |
60 0
CODE X
CODE X

はじめに

前回の「初級から中級まで:配列(I)」では、プログラミングにおいて非常に重要かつ難解なトピックの一つである「配列」について話を始めました。配列は実際にはそれほど難しくないと感じる方もいらっしゃるかもしれませんが、この記事を読み進めていくうちに、なぜ私がこのトピックを「複雑で習得が難しい」と考えているのかが理解できると思います。なぜなら、配列という概念は、プログラミングの他の多くの概念の土台となるものだからです。

この概念が実際にどう応用できるかを私が説明し、実演することで、この記事の内容をしっかりと理解された読者の皆さんには、プログラミング言語に他の機能がなぜ存在するのかという理由が間違いなく見えてくるはずです。なぜなら、この基礎を理解してしまえば、他のすべてのことがぐっと習得しやすく、理解しやすくなるからです。

ここで本当に難しいのは、まだ取り扱っていない他のトピックに立ち入らずに説明をおこなうことです。つまり、なぜある要素が作られたのかということを示しつつも、それが何なのかを今はまだ明かさないということです。というのも、ツールそのものを理解することよりも、そのツールの背後にある考え方を理解することの方が遥かに重要だからです。多くのプログラマーはこの考え方を無視して、ツールの使い方ばかりに気を取られてしまいがちです。その結果、いずれ壁に突き当たります。なぜなら、問題を解決するのはツールではなく概念だからです。たとえば、金槌は釘を打つために使えますが、それ以外にも使い道があります。たとえば、何かを壊すためにも使えるのです。もちろん、壊すだけなら大槌のような別の道具のほうが向いているかもしれません。

この話に入る前に、この文章をしっかり理解するための前提があります。それは、「変数」と「定数」が何であるかを知り、理解していることです。


ROM型配列

配列を宣言する方法には基本的に2通りあります。1つは静的配列として宣言する方法、もう1つは動的配列として宣言する方法です。それぞれの使い方を理解すること自体は比較的簡単ですが、細かい違いや注意点によって、「静的配列」と「動的配列」が実際には何なのかという理解を難しくすることがあります。特に、CやC++のような他のプログラミング言語を考慮すると、さらに複雑になることがあります。しかし、ここMQL5においても、時に混乱を感じる場面があるかもしれません。なぜなら、静的配列と動的配列の最も本質的かつ重要な違いは、「コードの実行中にサイズを変更できるかどうか」にあるからです。

このように考えると、配列を「静的」か「動的」かで分類するのは簡単なように思えます。しかし、ここで思い出すべきなのは、文字列もまた配列であるという点です。ただし、これは特殊な種類の配列です。このことが、文字列を静的または動的と単純に分類するのを困難にしています。とはいえ、ここではこの事実には踏み込みません。文字列型には直接触れずに進めます。それによって混乱を避け、話題の本質を明確に保ちます。

本質的に(これは将来どのプログラミング言語を学んでも変わらない事実ですが)定数配列は常に静的配列です。どの言語を使おうとも、必ず静的になります。

では、なぜ「定数配列は常に静的である」と自信を持って言い切れるのでしょうか。その理由は、定数配列というものを、「ROMメモリ」と考える必要があるからです。この時点では、少しピンとこないかもしれません。というのも、私たちのアプリケーションは常にRAM領域で読み込まれ、実行されているからです。RAMでは読み書きが自由にできます。それなのに、どうして「ROMメモリ」のように読み取り専用である定数が存在しうるのでしょうか。これは一見、非論理的に思えるかもしれません。

だからこそ、定数と変数が何かをしっかり理解する必要があります。アプリケーションの観点から言えば、RAM内であってもROMのような振る舞いを実現することは可能なのです。それによって、アプリケーションの整合性や使いやすさが損なわれることはありません。実際、定数配列を使ってアプリケーション内に小さなROMを作るというのは、特に複雑なコードや絶対に変更してはならない特定の値を必要とするアプリケーションでは、よく見られる設計です。

たとえば、エラーや通知などを報告するメッセージ群を考えてみましょう。これらのメッセージを一箇所にまとめて、異なる言語に翻訳しておくことができます。そして、アプリケーションが起動されたときに、使用する言語を判別し、それに応じて「ROM的メモリ」を構築するのです。こうすれば、異なる言語環境のユーザーが同じアプリケーションを使うことが可能になります。これは、配列がROMのように振る舞う典型的な例です。しかし、もし翻訳をファイルから動的に読み込む設計にした場合、その配列はもはや純粋なROMではなくなります。あるいは、完全に静的とは言い切れなくなります。それでも、設計上はROM的、または静的として分類されることはあります。

この概念をより理解しやすくするために、そしてそれがライブラリ関数をどのように使いこなすかを左右する重要な基盤であるために、ここではその実例を取り上げて説明していきましょう。この概念を実際に示します。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     const char Rom_01[]  = {72, 101, 108, 111, 33};
07.     const char Rom_02[8] = {'H', 'e', 'l', 'o', '!'};
08. 
09.     PrintFormat("%c%c%c%c%c%c", Rom_01[0], Rom_01[1], Rom_02[2], Rom_01[2], Rom_02[3], Rom_01[4]);
10. }
11. //+------------------------------------------------------------------+

コード01

以下は、コード01を実行した結果です。

図01

これは非常に分かりやすい内容です。しかし、ここで注目すべきなのは6行目と7行目です。どちらのケースでも、見た目は同じ内容を持つROMが2つ作られています。ですが、それらのサイズがまったく異なっているのです。ただし、少し疑問が生まれるかもしれません。どういうことでしょう。私には理解できません。コードを見れば、どちらの配列も5つの要素を持っているように見えます。しかも、すべての要素は同一です。ただ、宣言方法が異なるだけです。でも、なぜこの2つのROMは異なるとされるのでしょうか。理屈に合いません。

おっしゃるとおりです、読者の皆さん。両方の配列とも、同じ要素を持っています。たしかに、宣言方法は違いますが。しかし、それらが異なるとされる理由は、まさにその宣言方法にあります。ここは非常に重要なポイントなので、しっかりと理解してください。

まず6行目にあるROM_01ですが、これは5つの要素を持つ配列です。こういった形式は、静的配列を宣言するときによく使われる典型的な形です。括弧[]の中にサイズが書かれていないことに注目してください。ここで多くの人が混乱するのですが、定数配列においては、括弧内にサイズを明記しないのは「普通のこと」です。むしろ、よく見られる形です。なぜなら、この書き方をすることで、実装時に値の数を柔軟に決めることができるからです。そして、一度その値が設定されると、配列のサイズは完全に固定され、変更不可能になります。結果として、この配列は「5つの要素からなる静的配列」として確定するのです。

一方で、ROM_02の場合は少し異なります。この配列でも、実際には5つの要素が明示的に宣言されています。しかし、配列のサイズとして「8」が指定されているため、残りの3つの要素は未定義のままです。これらの未定義の要素には注意が必要です。なぜなら、これらの要素には正しい値が入っている可能性もありますし、意味不明なゴミのようなデータが入っている可能性もあるからです。それは、コンパイラが配列をどのように初期化するかに依存します。とはいえ、ここで扱っているのは定数配列であるという点を忘れないでください。

とはいえ、どちらのケースでも、私たちが扱っているのは静的配列です。つまり、配列の要素数は、その配列が存在する間、絶対に変わりません。1つ目のケースでは要素数は常に5、2つ目では常に8です。そして、重要な基本ですが、配列のインデックスは常に0から始まります。したがって、カウントは常にゼロから始まります。

ここまでで、それぞれの配列に何個の要素があるかは分かりました。では、インデックス5の要素にアクセスしようとしたらどうなるでしょうか(繰り返しますが、インデックスは0始まりです)。この疑問を確かめるために、簡単なテストコードを実行してみましょう。そのために、コードを少し変更して、以下のような形にしてみましょう。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     const char Rom_01[]  = {72, 101, 108, 111, 33};
07.     const char Rom_02[8] = {'H', 'e', 'l', 'o', '!'};
08. 
09.     const uchar pos = 6;
10. 
11.     PrintFormat("%c%c%c%c%c%c", Rom_01[0], Rom_01[1], Rom_02[2], Rom_01[2], Rom_02[3], Rom_01[4]);
12. 
13.     PrintFormat("Contents of position %d of the ROM_02 array: %d", pos, Rom_02[pos - 1]);
14.     PrintFormat("Contents of position %d of the ROM_01 array: %d", pos, Rom_01[pos - 1]);
15. }
16. //+------------------------------------------------------------------+

コード02

ここで注目すべきなのは、コード02の9行目です。この行では、どの要素にアクセスするのかを定数で指定しています。今回は、6番目の要素、つまりインデックス5の位置にある要素を取得しようとしています。一見すると、配列には5つの要素が宣言されているため、5番目の位置、すなわちインデックス5の要素にアクセスしても問題がないように思えるかもしれません。そして、13行目と14行目では、どちらも同じ値、たとえば33が表示されると考えるのが自然かもしれません。しかし、この考え方は正しくありません。配列のインデックスは0から始まるため、5番目に宣言された要素はインデックス4になります。したがって、インデックス5を指定すると、何かが起こります。結果を以下に示します。

図02

ここでは、2つの奇妙なことが起きている点に注目する必要があります。1つ目は、13行目が実行され、結果として0が表示されていることです。これは、7行目で宣言された配列には隠れた値が含まれていることを示しています。すしかし、重要なのは図02に表示されたエラーメッセージです。このメッセージは、コード02の14行目で、配列の範囲外にアクセスしようとしていることを示しています。

14行目で使用されているのは6行目で宣言された配列です。そのため、なぜエラーが発生するのか疑問に思うかもしれません。配列には5つの要素があるため、5番目の位置にアクセスすることは可能なはずだと思えるからです。しかし、再度強調しますが、インデックスは0から始まります。

この事実を確認するために、次に変更を加えます。その変更とは、コード02の9行目にある数値「6」を「5」に書き換えることです。このように修正すると、結果はすべて変わってきます。

const uchar pos = 5;

上記のように修正したコード02を再度コンパイルして実行すると、端末には次のような結果が表示されます。

図03

コードが正しく内容を表示できたことにお気づきでしょうか。これで、配列へのアクセス方法や、配列の宣言のわずかな違いが全く異なる結果をもたらすことを理解できると思います。

ROM型の配列は常に静的であり、見た目上動的に宣言されていてもそう変わりません。書き込みを試みるとコンパイルエラーになるこの種の配列については、これ以上語ることはあまりありません。ですので、ここでは一旦話を切り替え、もう少し複雑な別の種類の配列について説明していきます。こちらは書き込みが可能な配列です。


RAM型配列

この種類の配列を理解するための前提条件は、実はROM型配列を理解していることです。ROM型配列では、宣言は静的であったり動的であったりします。しかし、ROM型配列は内容が一定で要素数も固定されているのに対し、RAM型配列は少し複雑になります。というのも、要素数が固定でない場合もあれば固定の場合もあり、さらにコードの実行中に各要素の内容が変化する可能性があるからです。

これらの説明を聞くと、この種類の配列は厄介だと思われるかもしれません。しかし、正しく使うためには少しだけ注意が必要なだけです。ここでは、細部が非常に重要になります。ただし、MQL5にはバッファとして機能する特別な種類の配列が存在し、今回はそれについては触れません。これはMQL5特有のものです。この種類の配列については、MetaTrader 5向けの計算やプログラミングを説明する際に改めて詳しく解説します。したがって、ここで説明する内容はこのバッファ型配列には当てはまりません。今回の目的は、MQL5に限らずプログラミング言語全般における配列の一般的な説明をすることです。

では、前回のトピックで見た最後のコードを基にして、両方の宣言から「const」という予約語を外してみましょう。こうすることで配列に課せられていた制限がなくなります。ただし、この変更をおこなう際に非常に重要なポイントがあるので、次に示すコードで説明します。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     char Ram_01[]  = {72, 101, 108, 111, 33};
07.     char Ram_02[8] = {'H', 'e', 'l', 'o', '!'};
08. 
09.     const uchar pos = 5;
10. 
11.     PrintFormat("%c%c%c%c%c%c", Ram_01[0], Ram_01[1], Ram_02[2], Ram_01[2], Ram_02[3], Ram_01[4]);
12. 
13.     PrintFormat("Contents of position %d of the RAM_02 array: %d", pos, Ram_02[pos - 1]);
14.     PrintFormat("Contents of position %d of the RAM_01 array: %d", pos, Ram_01[pos - 1]);
15. }
16. //+------------------------------------------------------------------+

コード03

このコード03を実行すると、図03と同じ結果が得られます。しかし、ここには根本的な違いがあります。それは、私たちがROMではなくRAMを扱っているという点です。これにより、コード03を以下のように変更することが可能になります。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     char Ram_01[]  = {72, 101, 108, 111, 33};
07.     char Ram_02[8] = {'H', 'e', 'l', 'o', '!'};
08. 
09.     uchar pos = 5;
10. 
11.     PrintFormat("%c%c%c%c%c%c", Ram_01[0], Ram_01[1], Ram_02[2], Ram_01[2], Ram_02[3], Ram_01[4]);
12. 
13.     PrintFormat("Contents of position %d of the Ram_02 array: %d", pos, Ram_02[pos - 1]);
14.     PrintFormat("Contents of position %d of the Ram_01 array: %d", pos, Ram_01[pos - 1]);
15. 
16.     Ram_02[pos - 1] = '$';
17.     PrintFormat("Contents of position %d of the Ram_02 array: %d", pos, Ram_02[pos - 1]);
18. 
19.     Ram_01[pos - 1] = '$';
20.     PrintFormat("Contents of position %d of the Ram_01 array: %d", pos, Ram_01[pos - 1]);
21. }
22. //+------------------------------------------------------------------+

コード04

このコード04は非常に興味深く、ある意味で少し好奇心をそそるものです。これは単純に、16行目と19行目で配列の特定の位置にある値を変更しようとしているためです。この点をしっかり理解すると、より挑戦的な読者の好奇心を刺激することでしょう。コード04を実行すると、結果は以下のように表示されます。

図04

値を実際に変更できたことに気づいたでしょう。もしROM型配列を使っていた場合は、これは許されません。この時点で、配列内の情報を自由に変更できるのか疑問に思うかもしれません。その答えは、「場合による」です。

ここで注意してほしいのは、6行目の配列は動的配列ですが、要素数を明示していないため、宣言時に初期化されていることから静的配列の性質を持っています。一方、7行目の配列は完全に静的で、宣言時に初期化する要素数よりも多くの要素を持つ可能性があります。

これを理解すると、要素を指すインデックスを使えば、その値を変更できることが分かるかもしれません。..ですよね。その通りです。実際、以下に示すようなコードを使用することもできます。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     char r[10] = {72, 101, 108, 108, 111, 33};
07. 
08.     string sz0;
09. 
10.     sz0 = "";
11.     for (uchar c = 0; c < ArraySize(r); c++)
12.         sz0 = StringFormat("%s%03d, ", sz0, r[c]);
13. 
14.     Print(sz0);
15. 
16.     Print("Modifying a value...");
17. 
18.     r[7] = 36;
19. 
20.     sz0 = "";
21.     for (uchar c = 0; c < ArraySize(r); c++)
22.         sz0 = StringFormat("%s%03d, ", sz0, r[c]);
23. 
24.     Print(sz0);
25. }
26. //+------------------------------------------------------------------+

コード05

コード05を実行すると、結果は下の画像のようになります。

図05

つまり、実際に動作します。配列内に存在する要素にアクセスしている限り、どの値でも変更可能です。ただし、18行目の操作が成功したのは、6行目で配列の要素数を10と指定していたためです。

ここまでの内容は理解できたと思います。しかし、配列宣言時には必ず要素数か要素そのものを指定しなければならないのでしょうか。この点は初心者の方にとってよく疑問に思われるところです。そして再び答えは「場合による」です。

これは重要なポイントなので覚えておいてください。定数配列(つまりROM型配列)は、作成時に必ず要素か要素数のいずれかが宣言されていなければなりません。この場合、要素を宣言することは必須ですが、要素数を明示的に指定するのは任意です。

このルールは厳格で、変更や解釈の余地はありません。一方でRAM型配列については、目的や使い方によって自由に扱うことができます。ただし、動的配列(ROM型や定数でない配列)に要素を宣言することは一般的ではありません。先ほどのコード04の6行目がその例です。要素を宣言してしまうと、実質的にその動的配列は静的配列に変わってしまうからです。これは同じコード04の7行目の配列の場合と似ています。

ただし、コードの解釈としては、コード04の6行目で宣言された配列は、以下のように置き換えることも可能です。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     char  Ram_01[5],
07.           pos = 5;
08.     
09.     Ram_01[0] = 72;
10.     Ram_01[1] = 101;
11.     Ram_01[2] = 108;
12.     Ram_01[3] = 111;
13.     Ram_01[4] = 33;
14. 
15.     PrintFormat("%c%c%c%c%c%c", Ram_01[0], Ram_01[1], Ram_01[2], Ram_01[2], Ram_01[3], Ram_01[4]);
16. 
17.     PrintFormat("Contents of position %d of the Ram_01 array: %d", pos, Ram_01[pos - 1]);
18.     Ram_01[pos - 1] = '$';
19.     PrintFormat("Contents of position %d of the Ram_01 array: %d", pos, Ram_01[pos - 1]);
20. }
21. //+------------------------------------------------------------------+

コード06

コード06の6行目では、静的配列の宣言がされています。これにより、コンパイラは自動的にメモリのブロックを確保します。このメモリ確保はコンパイル時におこなわれ、指定した要素数分の領域が予約されます。今回の場合は5つの要素です。型が「char」のため、5バイト分のメモリが確保され、すぐに使用可能な状態になります。

この確保された領域は、5バイトの変数として考えることができます。用途に応じて自由に使うことができます。たとえば、6行目の5の代わりに100を指定すれば、100バイト分の変数を作成したことになります。なお、現在の最大幅は8バイト(64ビット)なので、100バイトの配列には8バイト変数を12個以上格納できる計算になります。

いずれにしても、コード06を実行すると、以下に示す結果が表示されます。

図06

配列の宣言時ではなく、9行目から13行目の間で初期化がおこなわれていることに注意してください。これはコード06の18行目と同様の動作であり、これまでの例でも同じ方法でおこなわれてきました。

ただし、ここで1つ小さな注意点があります。配列が静的に作成されているため、何らかの理由でより多くのメモリが必要になっても、実行時に追加のメモリを割り当てることはできません。この制限はランタイムで発生します。したがって、静的配列は非常に限定的な状況で使われることが多いです。動的配列にも同様のことが言えます。では、コード06の6行目で見られるように、配列の要素数を明示的に指定する代わりに、以下のように宣言した場合はどうなるでしょうか。

    char  Ram_01[]

これは完全に動的な配列の宣言となります。ここでよく注意してください。こうすることで、プログラマである私たちが配列の管理、すなわち必要に応じてメモリの割り当てや解放をおこなう責任を負うことになります。しかし、9行目が実行されるとすぐにエラーが発生します。これは、まだ割り当てられていないメモリにアクセスしようとしているためです。このエラーの様子は以下の通りです。

図07

変数が多いプログラムでは、この種のエラーに遭遇することは珍しくありません。しかし、修正は簡単です。配列を使う前にメモリを割り当て、使い終わったら明示的にメモリを解放すればよいのです。この明示的な解放は良いプログラミング習慣とされています。

簡単なプログラムやあまり専門的でないコードでは、この解放処理が省略されることもありますが、推奨されません。割り当てられたメモリは、使わなくなったからといって自動的に解放されるわけではないからです。そのため、最初からメモリの割り当てと解放を正しくおこなう習慣を身につけておくのが望ましいです。そうすることで、コードの実行時に予期しないエラーが起こるのを防げます。

最後に、動的配列を正しく使用するためにコードを修正する方法を見てみましょう。コード06をコード07に置き換えます。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     char   r[];
07.     string sz0;
08.     
09.     PrintFormat("Allocated enough position for %d elements", ArrayResize(r, 10));
10. 
11.     r[0] = 72;
12.     r[1] = 101;
13.     r[2] = 108;
14.     r[3] = 111;
15.     r[4] = 33;
16. 
17.     sz0 = "";
18.     for (uchar c = 0; c < ArraySize(r); c++)
19.         sz0 = StringFormat("%s%03d, ", sz0, r[c]);
20.     Print(sz0);
21. 
22.     Print("Modifying a value...");
23. 
24.     r[7] = 36;
25. 
26.     sz0 = "";
27.     for (uchar c = 0; c < ArraySize(r); c++)
28.         sz0 = StringFormat("%s%03d, ", sz0, r[c]);
29.     Print(sz0);
30. 
31.     ArrayFree(r);
32. }
33. //+------------------------------------------------------------------+

コード07

このコードスニペットは、まさに純粋な動的配列を活用しています。ご覧のとおり、コード05と非常によく似ていますが、これは意図的なものです。この類似性は、異なる方法でも同じ結果を得られることを示すために設計されています。コード07を実行した結果を以下に示します。

図08

ただし、図08の強調表示された部分にぜひ注目してください。この部分は非常に重要です。通常、あまり注意を払わないプログラマーや、重要でない用途のコードを書く人たちは、メモリを適切に初期化せずに使用してしまいます。このような見落としは、経験豊富な開発者にとっても発見が難しい多くのバグを引き起こします。ここでは、その一例を示しています。

コード07の9行目でメモリが確保されています。11〜15行目の間で、確保されたメモリの一部の位置に値を代入しています。しかし、メモリから内容を読み取ろうとすると、奇妙なデータが見られます。このデータは「ガベージ」と呼ばれ、メモリ上に存在しているにも関わらず、本来あってはならないものです。

重要なのは22行目です。この時点でメモリを変更しようとしていることが示されています。これは24行目で実行されています。しかし、その後再びメモリの内容を読み取ると、まるでプログラムが未来を予知したかのようです。値を適用する前から、その値がどこからともなく現れたかのように見えます。まるで魔法のようです。

なぜこのようなことが起こるのでしょうか。もちろん、これは魔法でもタイムトラベルでもありません。理由は、確保されたメモリが完全にあなたの管理下にあるわけではないからです。メモリの割り当てはOSが管理しており、そのメモリの内容は何でもあり得ます。以前のプログラムの残骸(ガベージ)だったり、すべてゼロだったりと、内容は常にランダムです。さらに、OSが同じメモリ領域を連続して割り当てる場合、その間にメモリが解放され再割り当てされることがあれば、ガベージが残っている可能性は非常に高いです。

もし、そのメモリの内容が特定の値であると想定してしまうと、非常に解決が困難な問題に直面します。ですので、メモリの内容が特定の値であると決して仮定してはいけません。必ず、メモリを割り当てたら、その領域をクリアするか、少なくとも完全に初期化してください。MQL5ではこれをおこなう方法がいくつかありますが、個人的には標準ライブラリのシンプルで効率的な関数を使うのが好みです。修正後のコード07を以下に示します。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     char   r[];
07.     string sz0;
08.     
09.     PrintFormat("Allocated enough position for %d elements", ArrayResize(r, 10));
10. 
11.     ZeroMemory(r);
12. 
13.     r[0] = 72;
14.     r[1] = 101;
15.     r[2] = 108;
16.     r[3] = 111;
17.     r[4] = 33;
18. 
19.     sz0 = "";
20.     for (uchar c = 0; c < ArraySize(r); c++)
21.         sz0 = StringFormat("%s%03d, ", sz0, r[c]);
22.     Print(sz0);
23. 
24.     Print("Modifying a value...");
25. 
26.     r[7] = 36;
27. 
28.     sz0 = "";
29.     for (uchar c = 0; c < ArraySize(r); c++)
30.         sz0 = StringFormat("%s%03d, ", sz0, r[c]);
31.     Print(sz0);
32. 
33.     ArrayFree(r);
34. }
35. //+------------------------------------------------------------------+

コード08

コード08を実行すると、以下に示すような結果が表示されます。

図09

図08と図09の唯一の違いはメモリの場所です。最初の実行ではその場所にガベージデータが存在していました。しかし、コード08の11行目を追加したことで、ガベージデータが使われてしまうようなエラーは発生しなくなりました。これだけの違いです。ただし、MQL5には同じ目的で使える他の関数も存在します。どの関数を使うかは、あなたの選択と達成したい目的によって異なります。


最終的な考察

この記事では、ここで示した以上に複雑な内容の基礎に触れながら、より専門的な視点で配列に取り組む方法を説明しました。すべてを完全に理解できなかったかもしれませんが、この種のテーマは多くのプログラミング概念よりも説明が難しいため、ある程度は達成できたと考えています。具体的には、RAM上でROMのような振る舞いを作り出す方法や、動的配列と静的配列の使い分けの判断基準について解説しました。

とはいえ、重要な点として強調したいのは、配列やメモリアクセスに関しては決して何も前提としないことです。初期化されていない要素にはガベージが含まれている可能性があり、それをコード内で使用すると期待した結果を大きく損なったり、複数プログラム間のやりとりのトラブルを解決しづらくしてしまいます。

付録として、この記事で扱ったコードをすべて添付していますので、ぜひ学習と実践に役立ててください。失敗例の扱い方や、実務的なタスクにおける配列の活用方法を探求する助けになるはずです。

MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/15472

添付されたファイル |
Anexo.zip (3.32 KB)
EAのサンプル EAのサンプル
一般的なMACDを使ったEAを例として、MQL4開発の原則を紹介します。
取引におけるニューラルネットワーク:相対エンコーディング対応Transformer 取引におけるニューラルネットワーク:相対エンコーディング対応Transformer
自己教師あり学習は、ラベル付けされていない大量のデータを分析する効果的な手段となり得ます。この手法の効率性は、モデルが金融市場特有の特徴に適応することで実現され、従来手法の有効性も向上します。本記事では、入力間の相対的な依存関係や関係性を考慮した新しいAttention(注意)機構を紹介します。
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法 エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
この記事では、MT4において複数のEAの衝突をさける方法を扱います。ターミナルの操作、MQL4の基本的な使い方がわかる人にとって、役に立つでしょう。
取引におけるニューラルネットワーク:制御されたセグメンテーション 取引におけるニューラルネットワーク:制御されたセグメンテーション
この記事では、複雑なマルチモーダルインタラクション分析と特徴量理解の方法について説明します。