2019年4月12日金曜日
MQL4 で配列用メモリを動的確保する方法
プログラムの動作中に配列長が決まるということはよくあります。
そういった場合、C++ では `new` 演算子で動的にメモリを確保するわけですが、MQL4 では `new` 演算子で配列用のメモリを確保することは出来ません。
その代わり、[ArrayResize()](https://docs.mql4.com/array/arrayresize) を利用することでメモリを動的確保することができます。
プログラムの動作中に配列長が決まるということはよくありますMQL だとそう多くはないかもしれないですが。
そういった場合、C++ では `new` 演算子で動的にメモリを確保するわけですが、MQL4 では `new` 演算子で配列用のメモリを確保することは出来ません。
その代わり、[ArrayResize()](https://docs.mql4.com/array/arrayresize) を利用することでメモリを動的確保することができます。
`include/Arrays` 以下にはオブジェクトを格納できる [CArrayObj](https://www.mql5.com/en/docs/standardlibrary/datastructures/carrayobj) と [CList](https://www.mql5.com/en/docs/standardlibrary/datastructures/clist) が定義されています。
どちらも大体同じような機能を提供しているのですが、何が違うのか見てみました。
結論から言うと、`CArrayObj` の方は内部で配列を持っているのに対し、`CList` は [CObject](https://www.mql5.com/en/docs/standardlibrary/cobject) の持つリンクリスト機能を使っています。
### CArrayObj
`CArrayObj` は一般的な配列同様に使用できます。
ランダムアクセスする場合などはこちらを使うと良いでしょう。
> 参考
>
> [MQL4 で可変長配列を使用する方法 | Strategy of C](https://strategyofc.blogspot.com/2018/10/mql4.html)
### CList
`CList` は内部にポジションを持っており、`GetNextNode()` のようにイテレーティブな処理をする場合、高速に処理できます。
全要素に対して一気に処理をかけるような処理に向いています。
以下に簡単な例を挙げます。
```mq4
#property strict
#include <Object.mqh>
#include <Arrays/List.mqh>
class Item : public CObject {
private:
const int m_value;
public:
Item(int value) : m_value(value) {
}
virtual ~Item() {
}
int GetValue() const {
return m_value;
}
virtual int Compare(const CObject *node, const int mode=0) const {
int diff = this.GetValue() - dynamic_cast(node).GetValue();
return (diff == 0) ? 0 : (diff > 0) ? 1 : -1;
}
};
CList m_item_list;
void OnStart()
{
for (int i = 0; i < 10; i++) {
int value = (i % 2 == 0) ? i : i + 10;
m_item_list.Add(new Item(value));
}
// 内部ポインタを最初にもどす
m_item_list.MoveToIndex(0);
Item* item;
// 「最後まで一気に処理する」みたいなのに向いている
while ((item = m_item_list.GetNextNode()) != NULL) {
printf("%d", item.GetValue());
}
}
```
なお、`CList` も `CArrayObj` と同様、Sort 済みでないと検索が出来ません。
また、`Compare()` の実装によっては検索結果が想定外になることもあるので気をつけてください。
> 参考
>
> [[MQL4] CArrayObj はソート済みじゃないと Search() の結果が -1 になってしまう](https://strategyofc.blogspot.com/2018/10/mql4-carrayobj-search-1.html)
[CArrayObj](https://www.mql5.com/en/docs/standardlibrary/datastructures/carrayobj) の `Insert()` で `Total` 以上の値を指定したらどのようになるかやってみました。
結果は、「どんなに大きな値を指定しても最後に挿入される」でした。
エラーが返ってこない仕様なのでそして、ソースコードも読めるので、そうだろうとは思ってたけど、念の為。
```mq4
// index 0〜9 の要素を追加
for (int i = 0; i < 10; i++) {
int value = (i % 2 == 0) ? i : i + 10;
m_item_array.Add(new Item(value));
}
Item* item = new Item(99);
// index 99?に挿入
// 実際は index 10 (最後)に挿入される
m_item_array.Insert(item, 99);
```
`Item` 等の実装は以下のリンク先に掲載したものと同じです。
> 参考
>
> [MQL4 で可変長配列を使用する方法 | Strategy of C](https://strategyofc.blogspot.com/2018/10/mql4.html)
[MQL4 で可変長配列を使用する方法](https://strategyofc.blogspot.com/2018/10/mql4.html) に書いたのですが、`Sort()` 済みの [CArrayObj](https://www.mql5.com/en/docs/standardlibrary/datastructures/carrayobj) から `Search()` でインデックスを取得しても正しいインデックスを返さない場合があるので注意が必要です。 具体的な例を以下に挙げます。
[MQL4 で可変長配列を使用する方法](https://strategyofc.blogspot.com/2018/10/mql4.html) で既に書いたのですが、以下のように `CArrayObj::Search()` を呼んでも正しくインデックスが返ってこない時があります。
`Item` 等の定義は [MQL4 で可変長配列を使用する方法](https://strategyofc.blogspot.com/2018/10/mql4.html) を参照してください。
```mq4
for (int i = 0; i < 10; i++) {
int value = (i % 2 == 0) ? i : i + 10;
m_item_array.Add(new Item(value));
}
// 途中に挿入
Item* item_100 = new Item(100);
m_item_array.Insert(item_100, 5);
// 今挿入したばかりのオブジェクトを検索する
int index = m_item_array.Search(item_100);
printf("index: %d", index);
// 結果
// index: -1
```
@MetaTrader 4 (Version 4.00 Build 1090) MQL4 では C++ の [std::vector](http://www.cplusplus.com/reference/vector/vector/) のような動的配列が標準で使用できますBuild 600 で入った変更を受けて作成された模様。 `Include/Arrays/` 以下に定義されており、2019/4/12 追記[CArray](https://www.mql5.com/en/docs/standardlibrary/datastructures/carray) 系クラスの中で使用されているメモリの動的確保について書きました。
[MQL4 で配列用メモリを動的確保する方法 | Strategy of C](https://strategyofc.blogspot.com/2019/04/mql4.html)
場合によっては、こちらを直接使った方が良いかもしれません。#include <Arrays/ArrayObj.mqh>のように include するだけで使用可能です。 今回はその中の [CArrayObj](https://www.mql5.com/en/docs/standardlibrary/datastructures/carrayobj) について簡単な使い方を書こうと思いますが、 [CArrayInt](https://www.mql5.com/en/docs/standardlibrary/datastructures/carrayint)、[CArrayChar](https://www.mql5.com/en/docs/standardlibrary/datastructures/carraychar) 等他の class も基本の思想は同じです。 公式のリファレンスを探したのですが、 MQL5 用のものしか見つけられませんでした。 ただ、確認した限り、MQL4 でも同様に使用できるようですMQL5 から逆移植されたものと思われる。 > 参考 > > [Standard Library / Data Collections - Reference on algorithmic/automated trading language for MetaTrader 5](https://www.mql5.com/en/docs/standardlibrary/datastructures) なお、完全に動作するソースコードは最後に載せておきます。
アプリケーション開発から組み込みまで手を出しているフリーランスのエンジニア
投資経験は10年以上
現在、完全自動売買を目指して研究中
当ブログ内の情報は環境等により動作が異なる場合があります。 情報は正確になるよう努めていますが、もし間違いを見つけた方はご一報いただけると幸いです。 なお、当ブログの情報によって損害が生じたとしても管理人は一切の責任を負いません。 全て自己責任でお願い致します。