기고글 토론 "MQL5 Cookbook: 빠른 데이터 액세스를 위한 연관 배열 또는 사전 구현"

 

새로운 기고글 MQL5 Cookbook: 빠른 데이터 액세스를 위한 연관 배열 또는 사전 구현 가 게재되었습니다:

이 문서에서는 고유 키로 요소에 액세스할 수 있는 특수 알고리즘에 대해 설명합니다. 모든 기본 데이터 유형을 키로 사용할 수 있습니다. 예를 들어 문자열이나 정수 변수로 나타낼 수 있습니다. 이러한 데이터 컨테이너는 일반적으로 사전 또는 연관 배열이라고 합니다. 보다 쉽고 효율적인 문제 해결 방법을 제공합니다.

이 글에서는 정보의 편리한 저장을 위한 클래스, 즉 연관 배열 또는 사전에 대해 설명합니다. 이 클래스를 사용하면 키를 통해 정보에 액세스할 수 있습니다.

연관 배열은 일반 배열과 유사합니다. 그러나 인덱스 대신 ENUM_TIMEFRAMES 열거 또는 일부 텍스트와 같은 고유 키를 사용합니다. 키를 나타내는 것은 중요하지 않습니다. 중요한 것은 키의 고유성입니다. 이 데이터 저장 알고리즘은 많은 프로그래밍 측면을 크게 단순화합니다.

예를 들어, 오류 코드를 받아 오류에 해당하는 텍스트를 인쇄하는 함수는 다음과 같을 수 있습니다.

//+------------------------------------------------------------------+
//| Displays the error description in the terminal.                  |
//| Displays "Unknown error" if error id is unknown                  |
//+------------------------------------------------------------------+
void PrintError(int error)
 {
   Dictionary dict;
   CStringNode* node = dict.GetObjectByKey(error);
   if(node != NULL)
      printf(node.Value());
   else
      printf("Unknown error");
 }
-->

이 코드의 특정 기능은 나중에 살펴보겠습니다.

연관 배열 내부 논리에 대한 직접적인 설명을 다루기 전에 데이터 저장의 두 가지 주요 방법인 배열과 목록에 대한 세부 사항을 고려할 것입니다. 사전은 이 두 가지 데이터 유형을 기반으로 하므로 특정 기능을 잘 이해해야 합니다. 1장은 데이터 유형에 대한 설명입니다. 두 번째 장은 연관 배열과 그것으로 작업하는 방법에 대한 설명에 전념합니다.

작성자: Vasiliy Sokolov

 

멋진 작업, 저자에게 찬사를 보냅니다! 이것은 MQ가 오래 전에 \MQL5\Include\Arrays에 포함했어야 하는 것으로, 다음 릴리스에서 라이브러리에 포함되기를 바랍니다. 그건 그렇고, MQL4에서도 모든 것이 잘 작동합니다. 다음은 첫 번째 테스트의 측정 값입니다. 본격적인 포인터가 없기 때문에 * 객체 대신 간단한 데이터 유형을 포함 할 수 없다는 것을 이해합니까? 아니면 이 문제를 해결할 수 있는 방법이 있나요?

2015.03.23 13:25:54.617 TestDict EURUSD,M1: 1000000 elements. Add: 1373; Get: 218
2015.03.23 13:25:52.644 TestDict EURUSD,M1: 950000 elements. Add: 1216; Get: 219
2015.03.23 13:25:50.833 TestDict EURUSD,M1: 900000 elements. Add: 1217; Get: 218
2015.03.23 13:25:49.069 TestDict EURUSD,M1: 850000 elements. Add: 1154; Get: 187
2015.03.23 13:25:47.424 TestDict EURUSD,M1: 800000 elements. Add: 1092; Get: 187
2015.03.23 13:25:45.844 TestDict EURUSD,M1: 750000 elements. Add: 1061; Get: 171
2015.03.23 13:25:44.320 TestDict EURUSD,M1: 700000 elements. Add: 1107; Get: 156
2015.03.23 13:25:42.761 TestDict EURUSD,M1: 650000 elements. Add: 1045; Get: 140
2015.03.23 13:25:41.304 TestDict EURUSD,M1: 600000 elements. Add: 1014; Get: 156
2015.03.23 13:25:39.915 TestDict EURUSD,M1: 550000 elements. Add: 920; Get: 125
2015.03.23 13:25:38.665 TestDict EURUSD,M1: 500000 elements. Add: 702; Get: 109
2015.03.23 13:25:37.693 TestDict EURUSD,M1: 450000 elements. Add: 593; Get: 93
2015.03.23 13:25:36.836 TestDict EURUSD,M1: 400000 elements. Add: 577; Get: 78
2015.03.23 13:25:36.025 TestDict EURUSD,M1: 350000 elements. Add: 561; Get: 78
2015.03.23 13:25:35.247 TestDict EURUSD,M1: 300000 elements. Add: 515; Get: 78
2015.03.23 13:25:34.557 TestDict EURUSD,M1: 250000 elements. Add: 343; Get: 63
2015.03.23 13:25:34.063 TestDict EURUSD,M1: 200000 elements. Add: 312; Get: 47
2015.03.23 13:25:33.632 TestDict EURUSD,M1: 150000 elements. Add: 281; Get: 31
2015.03.23 13:25:33.264 TestDict EURUSD,M1: 100000 elements. Add: 171; Get: 16
2015.03.23 13:25:33.038 TestDict EURUSD,M1: 50000 elements. Add: 47; Get: 16
 
VDev:

멋진 작업, 작성자에게 찬사를 보냅니다! 이것은 MQ가 오래 전에 MQL5/Include/ Arrays에 포함했어야 하는 기능으로, 다음 릴리스에서 라이브러리에 포함되기를 바랍니다. 그건 그렇고, MQL4에서도 모든 것이 잘 작동합니다. 다음은 첫 번째 테스트의 측정 값입니다. 본격적인 포인터가 없기 때문에 * 객체 대신 간단한 데이터 유형을 포함 할 수 없다는 것을 알고 있습니까? 아니면 작동하게 할 수 있는 방법이 있나요?

작동할 것입니다. 박싱/언박싱 메커니즘과 템플릿의 도움으로 가능합니다. 아이디어는 각 기본 유형이 KeyValuePairBase 컨테이너에 패킹된다는 것입니다. 해당 유형을 언패킹하고 반환하는 것은 GetObjectByKey 유형의 내부 함수에 의해 수행됩니다:

template<typename Type, typename T>
Type GetObjectByKey(T key);

기본 유형으로 작업한다고 해서 성능상의 이점이 있는 것은 아니지만 훨씬 더 편리하다는 점을 강조하는 것이 중요합니다.

 

이제 템플릿을 기반으로 CObject 대신 기본 MQL 유형 중 하나를 저장하는 CDictionaryBase를 만들려고 했습니다. 안타깝게도 이 함수는 템플릿 유형을 반환할 수 없습니다. 유감입니다:

//+------------------------------------------------------------------+
//|TestDictBase.mq5 |
//|저작권 2015, 바실리 소콜로프. |
//|http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#property version   "1.00"
#include <Dictionary.mqh>
#include <DictionaryBase.mqh>
//+------------------------------------------------------------------+
//| 스크립트 프로그램 시작 기능|
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   CDictionaryBase base;
   base.AddValue("Pi", 3.14159);
   double pi = (double)base.GetValueByKey("Pi");
   printf(DoubleToString(pi, 5));
   //base.AddObject(
  }
//+------------------------------------------------------------------+
could not deduce template argument #1    TestDictBase.mq5        19      29
could not deduce template argument #0    DictionaryBase.mqh      404     25
possible loss of data due to type conversion    DictionaryBase.mqh      133     10
possible loss of data due to type conversion    DictionaryBase.mqh      135     10
possible loss of data due to type conversion    DictionaryBase.mqh      137     10
...

//+------------------------------------------------------------------+
//| 키별로 객체를 반환합니다.|
//+------------------------------------------------------------------+
template<typename T, typename C>
C CDictionaryBase::GetValueByKey(T key)
  {
   if(!ContainsKey(key))
      return NULL;
   return m_current_kvp.GetValue();
  }

안타깝습니다.

따라서 각 기본 유형에 대해 기본 컨테이너를 만들거나 기본 유형의 컨테이너를 만들어야 합니다: CDouble, CLong, CInt 등입니다.

 
C-4:

이제 템플릿을 기반으로 CObject 대신 기본 MQL 유형 중 하나를 저장하는 CDictionaryBase를 만들려고했습니다. 불행히도 함수가 템플릿 유형을 반환하는 것을 허용하지 않기 때문에 실패했습니다.

할 수 있습니다. 그러나 반환 된 값의 유형은 자동으로 추론 할 수 없으며 실제로 컴파일러에 의해 작성됩니다.

의사 매개변수 형태의 작은 목발을 사용할 수 있습니다.

template<typename T, typename C>
C CDictionaryBase::GetValueByKey(T key, C)
{
   if(!ContainsKey(key))
      return NULL;
   return m_current_kvp.GetValue();
}
 
TheXpert:

그렇습니다. 그러나 반환된 값의 유형은 자동으로 추론할 수 없으며, 이는 컴파일러가 실제로 작성하는 것입니다.

의사 매개변수 형태의 작은 버팀목을 사용할 수 있습니다.

실제 버팀목은 어디에 있을까요?
 
C-4:
실제 버팀목은 어디에 있나요?
두 번째 매개변수가 추가되었습니다.
 
TheXpert:
두 번째 매개변수가 추가되었습니다.
이제 알겠습니다. 내일 확인하겠습니다.
 
성능을 비교해 보셨나요? 정렬된 문자열 배열에서 이진 검색보다 유리한 데이터 크기는 어느 정도부터 시작되나요?
 
Integer:
성능을 비교해 보셨나요? 정렬된 문자열 배열에서 이진 검색보다 유리한 점은 어느 데이터 크기부터 시작되나요?

정확한 테스트는 해보지 않았지만 제 관찰에 따르면 수만 개의 요소부터 속도 이점이 나타나기 시작합니다. 즉, 100~1만 개의 요소가 있는 일상적인 작업에서는 성능 이점을 얻을 수 없습니다.

여기서 또 다른 중요한 점은 컨테이너 작업의 편의성입니다. 요소를 검색하기 위해 추가 메서드를 작성할 필요가 없습니다. 사전을 사용하는 많은 일상적인 작업을 몇 배나 쉽게 구현할 수 있습니다. 인덱스를 검색하기 위해 요소를 생성한 다음 해당 인덱스로 필요한 요소를 검색하는 등의 작업을 할 필요가 없습니다.

추신: 여기서 성능은 요소를 삽입하고 검색하는 총 시간으로 측정해야 한다고 생각했지만요. 그리고 CArrayObj에서 정렬된 항목을 검색하는 것이 상당히 빠른 작업이라면 삽입은 정말 문제가 됩니다. 빠른 검색에는 정돈이 필요하기 때문에 삽입 가능성을 제거할 수 없으며 이로 인해 성능이 크게 저하됩니다.

 

매우 흥미롭고 사전이 매우 유용하고 사용하기 쉬운 데이터 정리 도구라는 것이 분명합니다.


공유해 주셔서 감사합니다.