同一の配列の中でデータの位置をずらす際にもMQL5のArrayCopy関数が使えることを最近知りました(私が知らなかっただけで、別に大した話ではないのですが、汗)。
以前は、別個の配列(たとえば、配列Aと配列B)の間でデータをコピーする時に使うものだと思っていました。そのため、1つの配列の中で要素を移動する時はfor文を使っていました。
たとえば、次のような感じです。要素数が5つのarrayという配列において、array[1]からarray[4]の値をarray[0]からarray[3]に移動する場合
for(int i = 0; i <= 3; i++) { array[i] = array[i + 1]; }
ずっと気になっていたのですが、このやり方は力技な感じがするんですよね。要素数がもっと多かったら、その数だけループさせることになりますから。
しかし、ネット上で見かけたコードによると、同一の配列の中でデータを移動するのにArrayCopyが使えると気づきました。
目次
ArrayCopyの説明
ArrayCopyに関するヘルプページを見てみます。
ArrayCopy 配列を別の配列に複製します。 int ArrayCopy( void& dst_array[], // 複製先の配列 const void& src_array[], // ソース配列 int dst_start=0, // 複製先の配列の開始インデックス int src_start=0, // ソース配列の開始インデックス int count=WHOLE_ARRAY // 要素の数 );
冒頭に「別の配列に」と書かれているんですよね。私はこの言葉を今まで信じていました・・・(遠い目)。
たとえば、次のように使います。
ArrayCopy(array_B, array_A, 0, 1, 4);
array_Aからarray_Bにデータをコピーします(第1引数と第2引数)。
データのコピー先の先頭位置はarray_Bの要素[0]です(第3引数)。
array_Aの要素[1]から4つの要素(array_A[1]~array_A[4])をコピーします(第4, 第5引数)
ある配列から別の配列にArrayCopy
上述したことをコードで行ってみます。
このコードでは、ある配列から別の配列にデータをコピーしています。
void OnStart() { // 配列Aと配列Bを用意 int array_A[5]; int array_B[5]; // 配列Aを初期化 array_A[0] = 10; array_A[1] = 20; array_A[2] = 30; array_A[3] = 40; array_A[4] = 50; // 配列Bを初期化 ArrayInitialize(array_B, 0); // 配列の要素を表示 for(int i = 0; i < 5; i++) printf("array_A[%d] = %d", i, array_A[i]); Print(""); for(int i = 0; i < ArraySize(array_A); i++) printf("array_B[%d] = %d", i, array_B[i]); // 配列Aの要素のうち、先頭要素以外の4つを配列Bにコピー ArrayCopy(array_B, array_A, 0, 1, 4); Print("\nArrayCopyを実行しました\n"); for(int i = 0; i < 5; i++) printf("array_B[%d] = %d", i, array_B[i]); }
実行結果
array_A[0] = 10 array_A[1] = 20 array_A[2] = 30 array_A[3] = 40 array_A[4] = 50 array_B[0] = 0 array_B[1] = 0 array_B[2] = 0 array_B[3] = 0 array_B[4] = 0 ArrayCopyを実行しました array_B[0] = 20 array_B[1] = 30 array_B[2] = 40 array_B[3] = 50 array_B[4] = 0
同一の配列の中でArrayCopy
このコードでは、1つの配列の中でデータをコピーしています。
void OnStart() { int array_A[5]; array_A[0] = 10; array_A[1] = 20; array_A[2] = 30; array_A[3] = 40; array_A[4] = 50; // 配列の各要素を表示 for(int i = 0; i < 5; i++) printf("array_A[%d] = %d", i, array_A[i]); // array_A[1]からarray_A[4]の値を、array_A[0]からarray_A[3]に移動 ArrayCopy(array_A, array_A, 0, 1, 4); Print("\nArrayCopyを実行しました\n"); for(int i = 0; i < 5; i++) printf("array_A[%d] = %d", i, array_A[i]); // 最終要素に値を代入 array_A[4] = 60; Print("\n最終要素に60を代入しました\n"); for(int i = 0; i < 5; i++) printf("array_A[%d] = %d", i, array_A[i]); }
実行結果
array_A[0] = 10 array_A[1] = 20 array_A[2] = 30 array_A[3] = 40 array_A[4] = 50 ArrayCopyを実行しました array_A[0] = 20 array_A[1] = 30 array_A[2] = 40 array_A[3] = 50 array_A[4] = 50 最終要素に60を代入しました array_A[0] = 20 array_A[1] = 30 array_A[2] = 40 array_A[3] = 50 array_A[4] = 60
定数の一部を関数に置き換えたコード
void OnStart() { int array[5]; array[0] = 10; array[1] = 20; array[2] = 30; array[3] = 40; array[4] = 50; // 配列の各要素を表示 for(int i = 0; i < ArraySize(array); i++) printf("array[%d] = %d", i, array[i]); // array[1]からarray[4]の値を、array[0]からarray[3]に移動 ArrayCopy(array, array, 0, 1, ArraySize(array) - 1); Print("\nArrayCopyを実行しました\n"); for(int i = 0; i < ArraySize(array); i++) printf("array[%d] = %d", i, array[i]); // 最終要素に値を代入 array[ArraySize(array) - 1] = 60; Print("\n最終要素に60を代入しました\n"); for(int i = 0; i < ArraySize(array); i++) printf("array[%d] = %d", i, array[i]); }
実行結果
array[0] = 10 array[1] = 20 array[2] = 30 array[3] = 40 array[4] = 50 ArrayCopyを実行しました array[0] = 20 array[1] = 30 array[2] = 40 array[3] = 50 array[4] = 50 最終要素に60を代入しました array[0] = 20 array[1] = 30 array[2] = 40 array[3] = 50 array[4] = 60
実際の使用例
私は、平均値を求めたり、急激な価格変化(上昇、下降)を調べるために、過去 N 本のローソク足情報を配列に保存しています。
たとえば、「実体の大きさ」「実体とヒゲの比率」「ヒゲも含めた全長」など。
ためしに、1本前のローソク足から5本前のローソク足の全長を配列の中に格納することを例にしてコードを書いてみます。
int sizeOfArray = 5; // 配列の要素数 double array[]; // 配列 double multiplier = MathPow(10, _Digits); // 価格をポイント数に変換するための乗数 //================================= // OnInit()の中で実行 //================================= // 配列のサイズをセット ArrayResize(array, sizeOfArray); // 5本前から1本前のローソク足の全長を配列に格納 for(int i = 0; i < ArraySize(array); i++) { int shift = sizeOfArray - i; array[i] = iHigh(NULL, 0, shift) * multiplier - iLow(NULL, 0, shift) * multiplier; } // 配列の中身は次のとおり // array[0] = 5本前の全長 // array[1] = 4本前の全長 // array[2] = 3本前の全長 // array[3] = 2本前の全長 // array[4] = 1本前の全長 //================================= // OnCalculate()の中で実行 //================================= // 新しいローソク足が発生した時に1回だけ処理 static datetime barTime = D'2000.01.01 00:00'; // ←初期値はテキトー if(barTime != iTime(NULL, 0, 0)) { // 2番目の要素から最終要素を残して、先頭要素を破棄 // (この例では、array[1]からarray[4]の値を、array[0]からarray[3]に移動) ArrayCopy(array, array, 0, 1, ArraySize(array) - 1); // 最終要素に新たな値を代入 array[ArraySize(array) - 1] = iHigh(NULL, 0, 1) * multiplier - iLow(NULL, 0, 1) * multiplier; barTime = iTime(NULL, 0, 0); // タイムスタンプを更新 }