プログラムの3つの基本動作「処理」「条件分岐」「繰り返し」について学んだので、
それを駆使してプログラムを作成する事が可能になりました。
ですがMQL4プログラミングを学習する為にはもう少し知っておかなければならない事があります。
変数配列
まずは変数配列についてです、これは
以前学んだ変数 の応用です。
変数 を使用する為には、
データ型 と変数名を決めて宣言して使える状態にする必要がある事は覚えていますよね?
ここで同じ使用目的で使用する
変数 を12個用意する事を考えてみましょう。
使用目的は同じですが、区別する為にラベル名に連番を付けると
こんな感じで12本の容器を用意して、それぞれラベルを用意して個別の名前を付ける事になります。
これをプログラムで書いてみると
ソースコード:
#property strict
void OnStart ()
{
int template_value1;
int template_value2;
int template_value3;
int template_value4;
int template_value5;
int template_value6;
int template_value7;
int template_value8;
int template_value9;
int template_value10;
int template_value11;
int template_value12;
}
クリップボードにコピー
凄い無駄な事をやっていると思いません?
まるでIT企業の・・・もう下りは聞き飽きましたよねw
この12個の
変数 がそれぞれ違う目的で使用されるのであればこの書き方で問題無いのですが、
今回は使用目的が同じ
変数 を12個宣言するので、こんな手間のかかる事はしません。
仮にこれが1万個必要だったら1万行コピペして全部名前を手作業で変更しますか?コンピュータを使っているのにそんな手作業をするなんて●呆ちゃんですよね?
そんな阿●ちゃんが現実のIT企業に沢山いますw
そしてそんな無駄な事を何の疑いも無く手作業でこなして残業をして、「仕事をやった感」で心を満たして残業代を稼ぐ所謂生活残業をする社員が沢山いますが、実は会社にはこういう無駄な作業というのが改善されずに残り続けます。
理由は無能な人の雇用を維持する為に、こういう無意味で謎な何の役にも絶たない業務というのが会社から提供されるからです。
ここで
変数配列 が出てきます。
配列という言葉自体の意味は分かりますよね?
配列は「並べること。並びぐあい。また、一定の仕方で並べたもの。」という意味なので、
変数配列 とは変数を並べるという事が想像出来ます。
現実世界でのイメージでは
こんな感じです。
変数配列 とは容器を並べるケースのようなもので、ラベルはケースに一つ貼ってあるだけです。
ケースに容器を12本並べて、ケースに「これはビールです」と記載しておけば「このケースの中に入っているのは全部ビールか」と分かりますよね?
スポンサーの関係で他社製品のラベルが見えるとマズイのでラベルを全て剥がしてしまったので、何の飲み物か分からない状態の容器でも、ケースにラベルを張っておけばこれが何の飲み物か理解出来ますよね?
これをプログラムで書くと
こうなります。
単品の変数宣言との違いは分かりましたでしょうか?
変数配列 宣言はこのように変数名の後に[]を付ける必要があります。
[]の中は要素数を指定します。要素数というのは配列の長さです。
この要素数を9に変更すれば、配列の長さは9になります。
つまりビール瓶が9本入るケースが用意されるという事です。
ちなみに変数名をtemplate_valueからtemplate_arrayに変更しましたが、
これは単純に変数名から「これは
変数配列 だ」という事が分かるようにする為です。
「Array」とは配列という意味です。
ところでプログラミング未経験者の人はちゃんと手を動かして説明した通りにプログラムを作成して動作させていますよね?
ただ見ているだけでは全く役に立たないので、面倒でも実際に手を動かして下さい。
変数配列の初期化
変数配列 も変数と同様に初期化をする必要があります。
当然、一つ一つ指定して初期化するわけではありません。
変数配列 を初期化するには
配列関数 というものを使用します。
「配列関数?そりゃなんじゃい?」っと思ったでしょう。詳細は後で説明しますので今は気にしないで下さい。
今はとにかく指示された通りに手を動かしてみて下さい。
それでは
配列関数 の
ArrayInitialize ()関数を使って変数配列を初期化しましょう。
「はぁ!?コラ!おいてめぇ~!ArrayInitialize()関数?そんなの聞いた事ねーぞ!このダボ!」
VIDEO
っと思ったでしょう。
言いたい事は分かりますが、
関数 についての説明は後でします。
とにかく今は何も考えずに指示された通りに手を動かして下さい。
今やっているのはこんな感じです。
template_arrayのラベルが貼ってあるケースまるごと酒屋さんに渡して、「それ全部洗っておいて(0で初期化しておいて)」とお願いしています。
「そんなもの自分で洗えよ!酒屋さんを何だと思ってるんだ!そもそも酒屋さんはそんな事してくれんわ!」と思うかもしれませんが、例えですので。
ビール瓶を洗う達人にお任せして、ビール瓶を全て洗って貰っている状態です。
ちなみに
初期化する配列を渡す時にtemplate_array[]では無く、template_arrayを渡しています。
template_array[]は宣言の時に「template_arrayは配列です」と宣言する為のものであって、使用する時は[]を付けません(詳細は次説明します)。
今回はtemplate_arrayというラベルを貼ったケースを丸ごと酒屋さんに渡しています。
ArrayInitialize ()という酒屋さんはケース丸ごとじゃないと仕事を受け付けてくれないからです。
このように
変数配列 を丸ごと全部渡す時はtemplate_arrayという変数配列名だけを指定します。
これでtemplate_arrayの
変数配列 の中身は全て0で初期化されました。
未だ中身の確認の説明をしていないので確認出来ませんがw
ところでプログラミング未経験者の人はちゃんと手を動かして説明した通りにプログラムを作成して動作させていますよね?
ただ見ているだけでは全く役に立たないので、面倒でも実際に手を動かして下さい。
配列要素へのアクセス
配列の特定の要素に対してのアクセス方法を説明します。
どういう事か具体的に説明すると
ビール瓶が並んでいますが、一番左のビール瓶の中身を確認する行為と同じです。
さて問題です、一番左のビール瓶の中身を確認してくるように他人に指示する場合、どのようにして指示するでしょうか?
まぁ人間相手ならそのまま伝えれば良いのですが、コンピュータという機械相手にそんな曖昧な指示は通用しません。
とりあえずプログラムでどうやるのかを見てみましょう。
これで変数配列の最初の要素の値をログ出力します。
追加したソースコードはこれですが、
Print ( template_array[0] );
クリップボードにコピー
説明をする前に、もう一つ見て欲しい事があります。
今度は2番目の要素に対してアクセスする場合です。
Print ( template_array[1] );
クリップボードにコピー
プログラムが何をやっているのかのイメージは
イメージを見ると感覚的に分かりますよね?
それでは説明に入ります。
変数配列 の特定の要素にアクセスする為には、
変数名の後ろに[]を付けて、[]の中に添え字(インデックス)を入れる必要があります。
この添え字(インデックス)というのは、
変数配列 宣言時の要素数とは異なるので注意が必要です。
個人的にインデックスの方が呼び易いのでインデックスと呼びます。
このインデックスというのは、配列の先頭から幾つオフセットさせるか?という意味を持っています。
例えば
2番目の要素にアクセスする時はインデックスは1 になりますが、イメージで見てみると
感覚的に分かりましたでしょうか?
オフセットさせる数を指定するので、当然
1番目の要素にアクセスする場合はインデックスは0 を指定します。
(ちなみにインデックスを0から数え始める事を
0オリジン(外部リンク) と呼びます)
オフセット数を指定するという事は最後の12番目の要素へのアクセスする場合、
Print ( template_array[11] );
クリップボードにコピー
インデックスは11になります。
ちなみにインデックスを12(変数配列の範囲外)にした場合。
たまたま隣にあった他人のワインを勝手に飲んでしまう行為と一緒です。
これを現実世界で行うと
泥棒はこうなります。
プログラムでも同様で、たまたま隣にあったデータを勝手に触ってしまう事になります。
コンピュータはプログラムを逮捕する事はありませんが、代わりに「君はルール違反をしたから追放します」という感じでプログラムを強制終了させます。
悪い例を実演してみましょう。
プログラムを実行すると
「array out of range in 'TestProgram.mq4'(16,26)」
と表示されて、プログラムが強制終了されます。
このエラーの意味は「TestProgram.mq4のソースコードの16行目の26列目で、配列の範囲外にアクセスしています。」
という意味です(直訳ではありません)。
これは重要な内容なので絶対に体得して下さい。
ここは
初心者や外注SESがデフォルトの様に間違える部分 です。
ここでインデックス12を指定してエラーを発生させてしまうと初心者である事がバレバレです。
逆に言えば外注SESがそのエラーを発生させてしまうプログラムを納品したら、
その外注SESの次の契約更新は絶対に止めてください 。
他社員の工数を無駄に奪い取って足を引っ張るだけの時間泥棒なだけでは無く、最悪会社の信頼を失う事に発展する場合があります。
ITのプロである筈のIT企業の社員でさえ日常的にこの不具合を発生させています
(単純に基礎知識不足・単体テストが不十分というか実はテストしていない・拘束時間で給与が貰える為プロである意識が希薄化しているからでしょうが・・・)。
ここで一つ疑問に思った人はいないでしょうか?
「インデックスを0で指定するくらいなら、指定しなくてもいいんじゃないか?」と思いませんでした?
前回説明しましたが、インデックスを付けないで変数配列名だけの場合は、
変数配列 を丸ごと渡してしまいます。
この謎の酒屋さんに見覚えはありませんか?
そういう事です。
まとめると
変数配列 宣言時の[]の中身は要素数(配列の長さ)を指定する。
変数配列 の個別要素へのアクセス時の[]の中身は、インデックス(配列の先頭からのオフセット数)を指定する。
「変数配列の宣言時」と「個別要素へのアクセス時」で
同じ[]を使っていますが、意味は異なるので要注意。
ところでプログラミング未経験者の人はちゃんと手を動かして説明した通りにプログラムを作成して動作させていますよね?
ただ見ているだけでは全く役に立たないので、面倒でも実際に手を動かして下さい。
配列要素の値を変更
それでは実際に
変数配列 の要素を使ってみましょう。
まずは
変数配列 の全要素の内容をログ出力するようにして、中身を確認出来るようにします。
まぁこんな外注SESみたいな事をしないで当然forループを使います。
結果:
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
クリップボードにコピー
インデックス指定に変数を用いる事が可能なので、
forループ のループカウント用変数をインデックスに指定しています。
もし変数配列を使わずに全て個別の変数名を使っていたら、こういう事は出来ません。
配列要素の中身が確認出来るようになったので、配列要素の値を変更してみましょう。
結果:
TestProgram EURUSD,H1: 1
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
TestProgram EURUSD,H1: 0
クリップボードにコピー
変数配列 の先頭の要素に1という値が入りました。
ソースコード:
template_array[0] = 1;
クリップボードにコピー
やっている事は変数へ値を代入するのと同じです。
変数配列 の個別要素を指定した場合、単独の変数のように扱う事が出来ます。
ところでプログラミング未経験者の人はちゃんと手を動かして説明した通りにプログラムを作成して動作させていますよね?
ただ見ているだけでは全く役に立たないので、面倒でも実際に手を動かして下さい。
範囲外アクセスしない為に、配列サイズを取得する(ArraySize)
先程のサンプルソースは、範囲アクセスに関係するforループ内でハードコーディング(直値)を使用しているので、
実は良いサンプルソースではありません。
ソースコード:
for ( int icount = 0; icount < 12; icount++ ) {
クリップボードにコピー
今は初心者に分かり易いようにあえてハードコーディングを使用していますが、実はこれプログラミング未経験者の外注SESがよくやりがちなソースコードです。
作った時はこれで動作するかもしれませんが、変更が入った時に変更漏れがあって範囲アクセスしてしまう鉄板パターンです。
配列宣言とforループのハードコーディングに
define マクロを使用する方法もありますが、もっと良い方法があるのでそちらを紹介します。
defineマクロの場合、動的配列に対して使えないからです。
(突然defineマクロという謎用語が出てきましたが、これはあと(EA作成のあたり)で解説します)。
変数配列にアクセスする際に、その変数配列の要素数を取得する関数(
ArraySize )を使用します。
forループで直値の12を使用していた箇所をArraySize()関数に置き換え、関数の引数に要素数を知りたい変数配列を指定します。
ソースコード:
for ( int icount = 0; icount < ArraySize (template_array) ; icount++ ) {
クリップボードにコピー
ソースコード全体では以下の通りになります。
ソースコードを未だ見慣れていない初心者の方は、直接数値が指定されていた方が理解し易いかもしれませんが、
いずれはこういうバグを招き難いソースコードの書き方をしなければなりません。