Von der Grundstufe bis zur Mittelstufe: Struktur (VII)
Einführung
Im vorigen Artikel „ Von der Grundstufe zur Mittelstufe: Struktur (VI) haben wir untersucht, wie man eine universelle Implementierung einer einfachen Datenstruktur erstellen kann. Obwohl diese Art der Modellierung ungewöhnlich erscheint, wird sie viel häufiger verwendet, als die meisten Menschen denken. Das liegt daran, dass große Datenbanken ein sehr ähnliches Prinzip zur Organisation und Suche von Daten verwenden.
Ich verstehe, dass dieses Thema, das in unseren Artikeln behandelt wird, vielen übertrieben erscheinen mag. Wir sollten jedoch bedenken, dass unser Ziel darin besteht, die Dinge so zu erklären, dass in Zukunft keine Notwendigkeit besteht, bestimmte Details zu klären, die meiner Meinung nach trivial sind. Aber da die meisten von Ihnen diese Artikel nutzen, um von den Erfahrungen und dem Wissen anderer Programmierer zu lernen, wird alles, was ich Ihnen zu Beginn vermitteln kann, Ihnen, liebe Leser, in Zukunft sehr helfen.
Es reicht nicht aus, einen funktionierenden Code zu sehen; man muss auch verstehen, warum er funktioniert, und gegebenenfalls wissen, wie man eine von einem anderen Programmierer erstellte Implementierung an die eigenen Bedürfnisse anpasst. Dazu ist es notwendig, diese Konzepte zu kennen und zu verstehen, wie sie angewendet werden können.
Wie im vorigen Artikel haben wir mit der Implementierung von etwas sehr Interessantem begonnen, um besser zu verstehen, wie Strukturen innerhalb anderer Strukturen verwendet werden können. Vielleicht hat Sie das Ergebnis des letzten Codes neugierig gemacht. Dies geschah, weil wir eine für einen bestimmten Zweck geschaffene Struktur nutzten und sie schließlich auf einen anderen Zweck anwenden konnten. In diesem Fall waren die Daten ursprünglich vom Typ Double, aber am Ende konnten wir Daten beliebigen Typs verwenden.
Was wir gezeigt haben, ist jedoch nicht das, was wir erreichen wollten. Dazu müssen wir noch ein wenig weiter gehen. Aber all diese Anstrengungen werden sich in Zukunft auszahlen und belohnt werden. Das verspreche ich. Lassen Sie uns also dort weitermachen, wo wir im letzten Artikel aufgehört haben.
Strukturen von Strukturen, Teil zwei: die Rückkehr
Der vorherige Artikel endete mit dem unten abgebildeten Code.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. template <typename T> 05. struct st_Data 06. { 07. //+----------------+ 08. private: 09. //+----------------+ 10. struct st_Reg 11. { 12. T h_value; 13. uint k_value; 14. }Values[]; 15. //+----------------+ 16. string ConvertToString(T arg) 17. { 18. if ((typename(T) == "double") || (typename(T) == "float")) return DoubleToString(arg, 2); 19. if (typename(T) == "string") return arg; 20. 21. return IntegerToString(arg); 22. } 23. //+----------------+ 24. public: 25. //+----------------+ 26. bool Set(const uint &arg1[], const T &arg2[]) 27. { 28. if (arg1.Size() != arg2.Size()) 29. return false; 30. 31. ArrayResize(Values, arg1.Size()); 32. for (uint c = 0; c < arg1.Size(); c++) 33. { 34. Values[c].k_value = arg1[c]; 35. Values[c].h_value = arg2[c]; 36. } 37. 38. return true; 39. } 40. //+----------------+ 41. string Get(const uint index) 42. { 43. for (uint c = 0; c < Values.Size(); c++) 44. if (Values[c].k_value == index) 45. return ConvertToString(Values[c].h_value); 46. 47. return "-nan"; 48. } 49. //+----------------+ 50. }; 51. //+------------------------------------------------------------------+ 52. #define PrintX(X) Print(#X, " => ", X) 53. //+------------------------------------------------------------------+ 54. void OnStart(void) 55. { 56. const string T = "possible loss of data due to type conversion"; 57. const uint K[] = {2, 1, 4, 0, 7, 5, 3, 6}; 58. 59. st_Data <string> info; 60. string H[]; 61. 62. StringSplit(T, ' ', H); 63. info.Set(K, H); 64. PrintX(info.Get(3)); 65. } 66. //+------------------------------------------------------------------+
Code 01
Code 01 funktioniert zwar, ist aber etwas lästig. Das liegt daran, dass wir beim Kompilieren eine Antwort ähnlich der unten gezeigten erhalten.

Abbildung 01
Dinge wie diese (wie in Abbildung 01) verursachen einige Unannehmlichkeiten. Dies geschieht, weil Compiler-Meldungen je nach Art der Implementierung in der Zukunft zu Fehlern im Code führen können. Das liegt nicht daran, dass der Code nicht korrekt ist, sondern daran, dass der Compiler uns möglicherweise vor einem kritischen Fehler warnt. Wenn wir Fehlermeldungen ignorieren, kann es passieren, dass wir eine wirklich wichtige Nachricht übersehen.
In jedem Fall erhalten wir bei der Ausführung von Code 01 die folgende Antwort.

Abbildung 02
Interessant, nicht wahr? Doch bevor wir fortfahren, wollen wir das in Abbildung 01 dargestellte Problem lösen. Es gibt mehrere Möglichkeiten, dies zu tun. Letztendlich führen sie jedoch alle zum gleichen Ergebnis: einer expliziten Typumwandlung. Daher spielt es keine Rolle, auf welche Weise wir die ausdrückliche Typumwandlung erreichen. Entscheidend ist, dass sie stattfindet. Ändern wir dazu einfach Code 01 in etwa so:
. . . 15. //+----------------+ 16. string ConvertToString(T arg) 17. { 18. if (typename(T) == "double") return DoubleToString((double)arg, 2); 19. if (typename(T) == "float") return DoubleToString((float)arg, 2); 20. if (typename(T) == "string") return (string)arg; 21. return IntegerToString((long)arg); 22. } 23. //+----------------+ 24. public: 25. //+----------------+ . . .
Code 02
Wenn Sie sehen, dass die Lösung für das in Abbildung 01 gezeigte Problem lediglich in dem Codeausschnitt aus Code 02 besteht, sind Sie wahrscheinlich etwas enttäuscht. Viele haben wahrscheinlich erwartet, dass ein kompletter Satz von Anweisungen und Funktionen erstellt werden würde, und letztendlich müssen wir nur das tun, was in diesem Code 02 gezeigt wird. Das ist ein Teil des Prozesses. Wie enttäuscht wir sind, hängt in der Regel davon ab, wie viel wir ursprünglich erwartet hatten. Aber Spaß beiseite, kehren wir zum Code zurück, um zu verstehen, wie er erweitert werden kann, sodass er verschiedene Datentypen ohne wesentliche Änderungen verarbeiten kann.
Man könnte meinen, das sei kompliziert. Bevor ich Ihnen jedoch erkläre, wie man das macht, müssen Sie wirklich verstehen, wie und warum man ein Element eines Arrays mit einem anderen Element eines anderen Arrays verknüpft.
Das Konzept selbst ist sehr einfach. Es geht lediglich darum, eine Zuordnung zu erstellen. Mit anderen Worten: Wenn wir das Array-Element kennen, das als Schlüssel dienen soll, dann ist die Antwort im Wesentlichen ein Element eines anderen Arrays. Genau das werden wir mit Code 01 tun. Und warum nicht ein mehrdimensionales Array dafür verwenden? Der Grund dafür ist, dass ein mehrdimensionales Array in diesem Fall zwar die beste Option wäre, aber nicht geeignet ist, da wir es als Proof of Concept verwenden. Mit anderen Worten: Es geht darum, die Verwendung einer bestimmten Art von Sprachmitteln zu demonstrieren.
Nun gut, ich gehe davon aus, dass das, was wir tun, deutlich genug erklärt wurde, und ich möchte, dass Sie sich den Code 01 noch einmal ansehen und mir sagen, was seine Verwendung nicht nur für die Übersetzung von Werten, sondern auch in anderen ähnlichen Zusammenhängen verhindert.
Ziehen Sie keine voreiligen Schlüsse, sondern denken Sie zuerst darüber nach. Jetzt sehen wir uns eine der vielen Möglichkeiten an, den Code so zu verallgemeinern, dass wir beliebige Elemente darin unterbringen können. Wenn wir jedoch versuchen, den Code zu erweitern, werden wir feststellen, dass das Problem in der in Zeile 10 deklarierten Struktur liegt. Das ist das Problem, aber nicht in dem Sinne, wie Sie vielleicht denken.
Das Problem mit der Struktur in Zeile 10 ist, dass sie eine universellere Verwendung der Hauptstruktur verhindert. Da ich verstehe, dass dies komplex ist und Zeit braucht, werden wir mit dem Artikel fortfahren. Aber ich bitte Sie, diesen Artikel und die vorangegangenen zu lesen und zu wiederholen, um eine Sache zu verstehen.
Strukturen sind, wie andere Arten von Konstrukten, Formen der Darstellung mehr oder weniger komplexer Daten. Die Art und Weise, wie ein Problem zu lösen ist, hängt von seiner tatsächlichen Komplexität ab. Einige Aufgaben können mit diskreten und einfachen Datentypen gelöst werden, z. B. mit Gleitkommadarstellungen oder ganzzahligen Werten.
Wenn die Aufgabe komplexer wird, reichen diskrete Typen möglicherweise nicht mehr aus. In diesem Fall benötigen wir komplexere Datentypen, wie Arrays und Unions. Wenn das Problem komplex bleibt, brauchen wir einen ebenso komplexen Typ: Strukturen.
Die Struktur, die wir in Zeile 10 von Code 01 deklariert haben, ist also nichts anderes als ein komplexer Typ. Das muss man verstehen, um zu begreifen, was wir tun werden.
Zunächst muss diese Struktur aus Zeile 10 der in Zeile 04 definierten Struktur übernommen werden, wobei Code 01 erhalten bleibt. Auf diese Weise entsteht zusätzliche Komplexität, die eher verwirrend wirken kann, wenn Sie die vorherigen Artikel nicht verstanden haben oder ihnen nicht folgen konnten. Der Grund dafür ist, dass es keine Möglichkeit gibt, die Änderungen schrittweise umzusetzen, und dass dies in einer sehr kleinen Anzahl von Schritten geschehen muss. Der erste Schritt ist unten dargestellt:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. struct st_Reg 05. { 06. string h_value; 07. uint k_value; 08. }; 09. //+----------------+ 10. struct st_Data 11. { 12. //+----------------+ 13. private: 14. //+----------------+ 15. st_Reg Values[]; 16. //+----------------+ 17. public: 18. //+----------------+ 19. bool Set(const uint &arg1[], const string &arg2[]) 20. { 21. if (arg1.Size() != arg2.Size()) 22. return false; 23. 24. ArrayResize(Values, arg1.Size()); 25. for (uint c = 0; c < arg1.Size(); c++) 26. { 27. Values[c].k_value = arg1[c]; 28. Values[c].h_value = arg2[c]; 29. } 30. 31. return true; 32. } 33. //+----------------+ 34. string Get(const uint index) 35. { 36. for (uint c = 0; c < Values.Size(); c++) 37. if (Values[c].k_value == index) 38. return Values[c].h_value; 39. 40. return NULL; 41. } 42. //+----------------+ 43. }; 44. //+------------------------------------------------------------------+ 45. #define PrintX(X) Print(#X, " => ", X) 46. //+------------------------------------------------------------------+ 47. void OnStart(void) 48. { 49. const string T = "possible loss of data due to type conversion"; 50. const uint K[] = {2, 1, 4, 0, 7, 5, 3, 6}; 51. 52. st_Data info; 53. string H[]; 54. 55. StringSplit(T, ' ', H); 56. info.Set(K, H); 57. PrintX(info.Get(3)); 58. } 59. //+------------------------------------------------------------------+
Code 03
Achtung: Um den Code (und das, was wir tun werden) so einfach und klar wie möglich zu gestalten, gehen wir einen Schritt zurück und verwenden den Typ String. Bitte beachten Sie, dass die verallgemeinerte Version der Struktur, die wir aufgebaut haben, nicht mehr existiert und das Ausführungsergebnis immer noch dem Bild 02 entspricht.
Wenn Sie sich den Code 03 ansehen, könnten Sie vielleicht denken: „Okay, Sie sagten, es sei kompliziert, aber bis jetzt ist es noch einfach. Worin besteht die Komplexität?“ Nun, dann fahren wir fort. Der zweite Punkt, der geändert werden muss, ist, dass die Funktionen Set und Get (die Sie in den Zeilen 19 bzw. 34 sehen) KEINE Funktionen von st_Data sind; sie sind Funktionen von st_Reg.
Stellt sich die Frage: Was machen sie in st_Data? Die Antwort: Sie erschweren es der Struktur, sie universeller einzusetzen, was sie daran hindert, Daten jeglicher Art zu verarbeiten. Daher müssen wir diese Funktionen aus der Struktur st_Data, wie sie in Code 03 erscheint, entfernen. Der Einfachheit halber sehen wir uns an, wie das korrigierte Snippet aussehen wird. Es ist unten dargestellt:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. struct st_Reg 05. { 06. //+----------------+ 07. private: 08. //+----------------+ 09. string h_value; 10. uint k_value; 11. //+----------------+ 12. public: 13. //+----------------+ 14. void Set(const uint arg1, const string arg2) 15. { 16. k_value = arg1; 17. h_value = arg2; 18. } 19. //+----------------+ 20. uint Get_K(void) { return k_value; } 21. //+----------------+ 22. string Get_H(void) { return h_value; } 23. //+----------------+ 24. }; 25. //+----------------+ 26. struct st_Data 27. { 28. //+----------------+ 29. private: 30. //+----------------+ 31. st_Reg Values[]; 32. //+----------------+ 33. public: 34. //+----------------+ 35. bool Set(const st_Reg &arg) 36. { 37. if (ArrayResize(Values, Values.Size() + (Values.Size() == 0 ? 2 : 1)) == INVALID_HANDLE) 38. return false; 39. 40. Values[Values.Size() - 1] = arg; 41. 42. return true; 43. } 44. //+----------------+ 45. st_Reg Get(const uint index) 46. { 47. for (uint c = 0; c < Values.Size(); c++) 48. if (Values[c].Get_K() == index) 49. return Values[c]; 50. 51. return Values[0]; 52. } 53. //+----------------+ 54. }; 55. //+------------------------------------------------------------------+ . . .
Code 04
„Was ist das für ein Wahnsinn?“ Nur ruhig, es ist erst der Anfang. Jetzt haben wir nicht nur eine, sondern zwei Strukturen für die Kombination von Daten in der neuen Struktur st_Data. Doch bevor wir uns dem Hauptcode zuwenden (der in OnStart zu finden sein wird), müssen wir verstehen, was hier geschieht.
Obwohl dieses Codefragment auf die gleiche Weise wie Code 03 funktioniert, gibt es hier mehrere Aspekte, die sich von dem, was wir gewohnt sind, völlig unterscheiden. Dennoch handelt es sich meiner Meinung nach immer noch um einen Code für Anfänger, da wir es mit sehr einfachen Dingen zu tun haben.
Zunächst einmal ist es wichtig, sich daran zu erinnern, dass st_Reg eine Struktur ist, die für die Arbeit mit Strukturcode konzipiert wurde. Mit anderen Worten, wir werden nicht direkt mit den Variablen interagieren, aber wir werden einen Kontext schaffen, in dem wir sie verwalten können.
Im Wesentlichen funktioniert st_Reg genauso wie vor dem Einstieg in das Thema der verschachtelten Strukturen. Jetzt beginnt der Teil, der vielen Anfängern Schwierigkeiten bereitet, vor allem, wenn ihnen die grundlegenden Konzepte einer Variablen und der Arbeit mit Datentypen nicht klar sind. Verstehen wir nun, was in der zweiten Struktur namens st_Data geschieht. Da st_Reg ein komplexer Datentyp ist, können wir in Zeile 31 eine Variable deklarieren, die in diesem Fall ein Array ist, um Daten dieses Typs – also st_Reg – zu speichern. Bis jetzt sehen Sie wahrscheinlich keine Probleme, oder?
Wenn wir uns jedoch Zeile 35 anschauen, sehen wir, dass die Dinge anfangen, an Bedeutung zu verlieren, besonders wenn wir auf Zeile 37 achten. Meine Güte, was ist denn da los?! In dieser Zeile versuchen wir einfach, Speicherplatz zuzuweisen, liebe Leser. Es gibt jedoch ein kleines Detail, das die Sache ein wenig kompliziert macht. Tatsache ist, dass wir in MQL5 KEINE ZEIGER ZURÜCKGEBEN KÖNNEN. Daher gibt es in der in Zeile 45 gezeigten Funktion einen kleinen Nachteil, den wir in Zeile 37 zu beheben versuchen.
Also, aufgepasst. Wenn wir versuchen, einen in die Struktur st_Data eingefügten Wert zu finden und ihn nicht finden, müssen wir ungültige Daten zurückgeben. In Sprachen wie C und C++ wird dies in der Regel durch die Rückgabe eines Nullwertes erreicht. Hier in MQL5 jedoch KÖNNEN WIR DAS NICHT. Wir brauchen einen kleinen Trick. Wenn wir also in Zeile 37 versuchen, den ersten Block zuzuweisen, um Daten des Typs st_Reg hinzuzufügen, weisen wir zwei Speicherzellen zu. Die erste wird leer sein. Der zweiten wird jedoch der Wert zugewiesen, der der Zeile 40 entspricht.
Da dies das erste Element ist, werden ihm zwei Positionen zugewiesen. Ab dem nächsten Aufruf wird in Zeile 37 festgestellt, dass bereits mindestens zwei Elemente zugewiesen wurden, und es wird nur noch ein weiteres zugewiesen. Dies geschieht also bei jedem neuen Aufruf der Set-Funktion der st_Data-Struktur. Bitte beachten Sie, dass dies nichts mit der Set-Prozedur der st_Reg-Struktur zu tun hat, und dass es dafür einen Grund gibt, auf den wir später eingehen werden.
Da die Funktion „Get“ der Struktur st_Data direkt mit der Erläuterung der Struktur „Set“ zusammenhängt, müssen wir nur verstehen, dass der Wert, den die Funktion „Get“ als Argument erhält, nicht angibt, nach welchem Index im Array „Values“ gesucht wird, sondern welcher Wert physisch in diesem spezifischen Element gespeichert ist. Daher benötigen wir eine Schleife, um nach einem bestimmten Wert in der Struktur st_Reg zu suchen. Wir werden diese Frage später erörtern. Das war's im Allgemeinen.
Jetzt können wir den Hauptcode überprüfen. Wenn Sie sich erinnern, könnte der Code, den wir in dem Schnipsel aus Code 04 gesehen haben, sehr gut in einer Header-Datei platziert werden, und es würde ohne Probleme funktionieren. Aber bevor wir zeigen, wie man das macht, um den Code zu verallgemeinern, müssen wir uns den Rest ansehen. Es ist unten dargestellt:
. . . 55. //+------------------------------------------------------------------+ 56. #define PrintX(X) Print(#X, " => [", X, "]") 57. //+------------------------------------------------------------------+ 58. void OnStart(void) 59. { 60. const string T = "possible loss of data due to type conversion"; 61. const uint K[] = {2, 1, 4, 0, 7, 5, 3, 6}; 62. 63. st_Data info; 64. string H[]; 65. 66. StringSplit(T, ' ', H); 67. for (uint c = 0; c < H.Size(); c++) 68. { 69. st_Reg reg; 70. 71. reg.Set(K[c], H[c]); 72. info.Set(reg); 73. } 74. 75. PrintX(info.Get(3).Get_H()); 76. PrintX(info.Get(13).Get_H()); 77. } 78. //+------------------------------------------------------------------+
Code 05
Und damit beginnt der lustigste und aufregendste Teil. Da Sie den größten Teil dieses Codes wahrscheinlich schon verstanden haben, da er zuvor gezeigt wurde, lassen Sie uns zum neuen Teil übergehen. Dies betrifft insbesondere die Schleife in Zeile 67 sowie die Zeilen 75 und 76.
Beginnen wir mit der Schleife. Da jedes Element, das in die st_Data-Struktur eingefügt werden soll, vom Typ st_Reg ist, benötigen wir Zeile 71, um eine st_Reg-Struktur zu erstellen, sodass wir dann mit Zeile 72 ein Element vom Typ st_Reg in der st_Data-Struktur speichern können. Nun die Frage: Warum machen wir das nicht direkt in der st_Data-Struktur? Die Antwort ist, dass, wenn das Speichern von Elementen des Typs st_Reg direkt in der Struktur st_Data erfolgen würde, wir diese Struktur später nicht mehr erweitern könnten. Aber keine Sorge, Sie werden es bald verstehen.
Nachdem alle Elemente in die Struktur st_Data geschrieben wurden, können wir die Zeilen 75 und 76 verwenden, um diese Struktur zu testen. Dazu suchen wir in Zeile 75 nach einer Zeichenkette, deren Index drei ist. Und in den Zeilen 60 und 61 können Sie sehen, welchen Wert dieser Index hat. In Zeile 76 wird wiederum nach einem Element gesucht, dessen Index 13 ist. Da dieser Index in Zeile 61 nicht definiert ist, ist der zurückgegebene Wert der in Zeile 51 von Code 04 definierte.
Mit diesem Vorwissen können wir den Code ausführen. Daraus ergibt sich folgendes Ergebnis:

Abbildung 03
Mit anderen Worten: Es funktioniert perfekt. Jetzt können wir einen großen Teil des Codes verallgemeinern, aber dazu müssen wir uns einem neuen Thema zuwenden. Auf diese Weise können wir in Ruhe studieren, was wir bereits getan haben, und dann versuchen zu verstehen, was noch zu tun ist, um Verallgemeinerungen zu machen.
Strukturen von Strukturen, Teil drei, Rache
Wenn Sie die Informationen im vorherigen Thema verwirrend fanden, machen Sie sich bereit, denn jetzt wird es noch interessanter. Hier werden die Dinge wirklich komplexer. Beachten Sie also die folgende Tatsache. In Code 04 aus dem vorhergehenden Thema kann die Struktur st_Data nur einen Datentyp akzeptieren, nämlich den Typ st_Reg, aber das macht sie nicht universell. Dadurch wird die st_Data-Struktur auf das beschränkt, was in der st_Reg-Struktur definiert ist, auch wenn die beiden Strukturen nicht miteinander verbunden sind.
Wenn wir jedoch den Code der st_Data-Struktur geringfügig ändern, können wir ihre Ressourcen freigeben, sodass sie mit anderen Datentypen arbeiten kann. Beachten Sie, was ich gerade gesagt habe. Wenn die Struktur st_Data zu einem Template wird, ist sie nicht völlig frei von der Struktur st_Reg, die mit der Funktion Get verbunden ist, die an den Typ st_Reg gebunden bleibt. Doch die Mühe lohnt sich.
Wie bereits erwähnt, müssen wir lediglich die st_Data-Struktur in ein Template umwandeln und gleichzeitig den Hauptcode an dieses Template anpassen, die der Compiler dann generieren muss. Dies geschieht mit folgendem Code.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. struct st_Reg 05. { 06. //+----------------+ 07. private: 08. //+----------------+ 09. string h_value; 10. uint k_value; 11. //+----------------+ 12. public: 13. //+----------------+ 14. void Set(const uint arg1, const string arg2) 15. { 16. k_value = arg1; 17. h_value = arg2; 18. } 19. //+----------------+ 20. uint Get_K(void) { return k_value; } 21. //+----------------+ 22. string Get_H(void) { return h_value; } 23. //+----------------+ 24. }; 25. //+----------------+ 26. template <typename T> 27. struct st_Data 28. { 29. //+----------------+ 30. private: 31. //+----------------+ 32. T Values[]; 33. //+----------------+ 34. public: 35. //+----------------+ 36. bool Set(const T &arg) 37. { 38. if (ArrayResize(Values, Values.Size() + (Values.Size() == 0 ? 2 : 1)) == INVALID_HANDLE) 39. return false; 40. 41. Values[Values.Size() - 1] = arg; 42. 43. return true; 44. } 45. //+----------------+ 46. T Get(const uint index) 47. { 48. for (uint c = 0; c < Values.Size(); c++) 49. if (Values[c].Get_K() == index) 50. return Values[c]; 51. 52. return Values[0]; 53. } 54. //+----------------+ 55. }; 56. //+------------------------------------------------------------------+ 57. #define PrintX(X) Print(#X, " => [", X, "]") 58. //+------------------------------------------------------------------+ 59. void OnStart(void) 60. { 61. const string T = "possible loss of data due to type conversion"; 62. const uint K[] = {2, 1, 4, 0, 7, 5, 3, 6}; 63. 64. st_Data <st_Reg> Info_1; 65. string H[]; 66. 67. StringSplit(T, ' ', H); 68. for (uint c = 0; c < H.Size(); c++) 69. { 70. st_Reg reg; 71. 72. reg.Set(K[c], H[c]); 73. Info_1.Set(reg); 74. } 75. 76. PrintX(Info_1.Get(3).Get_H()); 77. PrintX(Info_1.Get(13).Get_H()); 78. } 79. //+------------------------------------------------------------------+
Code 06
Theoretisch kann Code 06 jede Art von Datenstruktur oder jeden Datentyp innerhalb der st_Data-Struktur enthalten. Theoretisch ist dies richtig. Der Grund liegt in der Funktion Get in Zeile 46 von Code 06. Um den Code universeller zu machen, mussten wir nur Zeile 26 hinzufügen und die Verweise auf st_Reg in den Zeilen 32, 36 und 46 durch den Typ T ersetzen. Danach mussten wir die Deklaration in Zeile 64 ändern, damit der Compiler die entsprechende Struktur erzeugen konnte.
Das Ergebnis der Ausführung von Code 06 ist das gleiche wie bei der Ausführung von Code 05, d. h. Abbildung 03. Allerdings sind wir in gewisser Weise immer noch an die st_Reg-Struktur gebunden, obwohl diese Bindung (die auch ein Problem darstellt) auch einige Vorteile bringen kann. Alles hängt von den Bedürfnissen, der Kreativität und dem Wissensstand der einzelnen Leser zu bestimmten Themen ab. Beachten Sie das Folgende. Die einzige verbleibende Verbindung zwischen den Strukturen st_Data und st_Reg ist die so genannte Get_K-Funktion, die sich in Zeile 49 befindet. Dies ist die einzige Verbindung, die noch besteht. Da die Struktur st_Reg auch überladen werden kann, können wir in diesem Fall verschiedene Typen verwenden.
Da solche Dinge aber zu komplexen Details führen können, die im letzten Teil des Artikels schwer zu verstehen sind, werden wir sie jetzt nicht erörtern.
Dennoch können wir etwas tun, das ich interessant finde und das auch im verbleibenden Teil des Artikels erläutert werden kann. Es handelt sich genau um Zeile 49 von Code 06. Und jetzt aufgepasst, liebe Leserinnen und Leser: Im Artikel „ Von der Grundstufe bis zur Mittelstufe: Überladen“ haben wir über eine sehr einfache und interessante Art und Weise gesprochen, Funktionen und Prozeduren zu überladen. In anderen Artikeln dieser Reihe haben wir auch gezeigt, dass dies mit Namen geschehen kann, die mit denen der Standardbibliothek identisch sind.
Jetzt kommt der interessante Teil: Was wäre, wenn wir in Code 06 eine Möglichkeit schaffen, die in Zeile 49 gezeigte Funktion Get_K zu überladen? Welche Möglichkeiten würden sich dadurch eröffnen? Wie wir bereits gesagt haben, sind Kreativität und die Anwendung von Konzepten in diesem Bereich sehr interessant. Wenn Sie nämlich genau hinsehen, werden Sie feststellen, dass die Struktur st_Reg (siehe Code 06) nur zwei Variablen enthält. Aber wir können die Struktur st_Reg erweitern, um eine andere Struktur mit mehr Variablen oder sogar mit völlig anderen Datentypen innerhalb der Struktur zu erhalten.
Auf diese Weise müssten wir jedoch nicht unbedingt die ursprüngliche Struktur st_Reg löschen oder die bereits in Code 06 erstellte Struktur st_Data ändern. Da ich mir nicht sicher bin, ob Sie verstehen, was ich meine, lassen Sie uns einen Codeschnipsel betrachten, an dem wir sehen können, was ich zu zeigen versuche. Dieser Code steht unten.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. struct st_Reg 005. { 006. //+----------------+ 007. private: 008. //+----------------+ 009. string h_value; 010. uint k_value; 011. //+----------------+ 012. public: 013. //+----------------+ 014. void Set(const uint arg1, const string arg2) 015. { 016. k_value = arg1; 017. h_value = arg2; 018. } 019. //+----------------+ 020. uint Get_K(void) { return k_value; } 021. //+----------------+ 022. string Get_H(void) { return h_value; } 023. //+----------------+ 024. }; 025. //+------------------------------------------------------------------+ 026. struct st_Bio 027. { 028. //+----------------+ 029. private: 030. //+----------------+ 031. string h_value; 032. string b_value; 033. uint k_value; 034. //+----------------+ 035. public: 036. //+----------------+ 037. void Set(const uint arg1, const string arg2, const string arg3) 038. { 039. k_value = arg1; 040. h_value = arg2; 041. b_value = arg3; 042. } 043. //+----------------+ 044. uint Get_K(void) { return k_value; } 045. //+----------------+ 046. bool Get_Bio(string &arg1, string &arg2) 047. { 048. arg1 = h_value; 049. arg2 = b_value; 050. 051. return true; 052. } 053. //+----------------+ 054. }; 055. //+------------------------------------------------------------------+ 056. template <typename T> 057. struct st_Data 058. { 059. //+----------------+ 060. private: 061. //+----------------+ 062. T Values[]; 063. //+----------------+ 064. public: 065. //+----------------+ 066. bool Set(const T &arg) 067. { 068. if (ArrayResize(Values, Values.Size() + (Values.Size() == 0 ? 2 : 1)) == INVALID_HANDLE) 069. return false; 070. 071. Values[Values.Size() - 1] = arg; 072. 073. return true; 074. } 075. //+----------------+ 076. T Get(const uint index) 077. { 078. for (uint c = 0; c < Values.Size(); c++) 079. if (Values[c].Get_K() == index) 080. return Values[c]; 081. 082. return Values[0]; 083. } 084. //+----------------+ 085. }; 086. //+------------------------------------------------------------------+ 087. #define PrintX(X) Print(#X, " => [", X, "]") 088. //+------------------------------------------------------------------+ 089. void CheckBio(st_Data <st_Bio> &arg) 090. { 091. string sz[2]; 092. 093. Print("Checking data in the structure..."); 094. for (uint i = 7; i < 11; i += 3) 095. { 096. Print("Index: ", i, " Result: "); 097. if (arg.Get(i).Get_Bio(sz[0], sz[1])) 098. ArrayPrint(sz); 099. else 100. Print("Failed."); 101. } 102. } 103. //+------------------------------------------------------------------+ 104. void OnStart(void) 105. { 106. const string T = "possible loss of data due to type conversion"; 107. const string M[] = {"2", "cool", "4", "zero", "mad", "five", "what", "xoxo"}; 108. const uint K[] = {2, 1, 4, 0, 7, 5, 3, 6}; 109. 110. st_Data <st_Reg> Info_1; 111. st_Data <st_Bio> Info_2; 112. 113. string H[]; 114. 115. StringSplit(T, ' ', H); 116. for (uint c = 0; c < H.Size(); c++) 117. { 118. st_Reg reg; 119. st_Bio bio; 120. 121. reg.Set(K[c], H[c]); 122. bio.Set(K[c], M[c], H[c]); 123. 124. Info_1.Set(reg); 125. Info_2.Set(bio); 126. } 127. 128. PrintX(Info_1.Get(3).Get_H()); 129. PrintX(Info_1.Get(13).Get_H()); 130. CheckBio(Info_2); 131. } 132. //+------------------------------------------------------------------+
Code 07
Wenn Sie den Code 07 ausführen, erscheint im Terminal ein Bild, das diesem sehr ähnlich ist.

Abbildung 05
Oh mein Gott! Was für eine wilde und verrückte Schöpfung wir da geschaffen haben! Beruhigen Sie sich, liebe Leserinnen und Leser, was uns hier interessiert, ist genau die in Bild 05 hervorgehobene Information, die durch die Prozedur in Zeile 89 erzeugt wurde, auf die wir unsere Erklärung konzentrieren werden, da der größte Teil des Codes leicht zu verstehen ist. Das Verständnis dieses Verfahrens in Zeile 89 ist entscheidend für das Verständnis dessen, was im nächsten Artikel behandelt wird.
Bevor wir jedoch erklären, was in der Prozedur in Zeile 89 geschieht, müssen wir einige zusätzliche Punkte verstehen. Beginnen wir mit Zeile 26, wo eine neue Struktur angelegt wird. Es ist anzumerken, dass sie eine ähnliche Grundlage wie die st_Reg-Struktur hat, aber die Ähnlichkeit endet hier, da die st_Bio-Struktur mehr Variablen und einen anderen Satz von Operationen in ihrem Kontext enthält. Was uns interessiert, ist die Funktion in Zeile 44. Der Grund dafür ist, dass sie die gleiche Funktion wie die Zeile 20 erfüllt, da sie die gleichen Ziele verfolgt. Und das ist wichtig für das, was wir als Nächstes tun werden.
Bitte beachten Sie auch, dass die Struktur st_Data (aus Code 07) genau der Struktur st_Reg in Code 06 entspricht. Die einzige wirkliche Änderung ist das Vorhandensein der Struktur st_Bio. Danach können wir zur Hauptprozedur OnStart übergehen, die den Einstiegspunkt für unseren Code darstellt. Beachten Sie, dass wir hier einige zusätzliche Elemente hinzugefügt haben, die im Code 06 nicht vorhanden waren. Diese Beispiele sollen gerade zeigen, wie flexibel die Struktur st_Data werden kann, wenn die Implementierungsphase des Codes sorgfältig durchdacht ist.
Im Wesentlichen haben wir die Zeilen 107 und 111 hinzugefügt. Bitte konzentrieren Sie sich jetzt. Beachten Sie, dass der Unterschied zwischen den Zeilen 110 und 111 genau in der Art der Daten besteht, die in die st_Data-Struktur eingefügt werden. Daher fügen wir innerhalb der Schleife in Zeile 116 eine neue Variable in Zeile 119 hinzu, die in Zeile 122 Werte erhält, und in Zeile 125 weisen wir der Struktur ein neues Datenelement zu oder fügen es hinzu. In diesem Fall unterscheidet sich der Datentyp vom Datentyp in Zeile 124. Bislang ist alles so eingetreten, wie im Code 06 vorhergesagt.
Hier beginnt jedoch die Komplexität und gleichzeitig der interessanteste Teil: Wir haben die Zeile 130, die zur Ausführung der Prozedur in Zeile 89 führen wird. Kommen wir nun zum interessantesten Teil. Bitte beachten Sie, dass wir uns in Zeile 94 in der Schleife befinden, und dass uns vor allem die Zeile 97 interessiert, die wir mit Zeile 128 vergleichen sollten. Worin besteht der Unterschied zwischen ihnen? Praktisch keine, außer dass, da Zeile 76 einen Strukturtyp zurückgibt, der sich auf den Datentyp der in Zeile 62 deklarierten Variablen bezieht, keine Unterschiede zwischen den beiden Zeilen bestehen.
In dem Artikel „Von der Grundstufe zur Mittelstufe: Variablen (III)“, haben wir erklärt, dass eine Funktion eine besondere Art von Variable ist. Hier wird dieses Konzept auf eine sehr interessante Weise angewandt, denn je nach der Antwort, die wir von der Funktion in Zeile 76 erhalten, können wir auf den einen oder anderen Datentyp verweisen, in diesem Fall auf eine Struktur. Ohne den Einsatz der strukturierten Programmierung wäre es praktisch unmöglich (oder äußerst schwierig), das zu schaffen, was hier gemacht wird. Der Punkt ist, dass in Zeile 97 ein Verweis auf die Struktur st_Bio verwendet wird. In Zeile 128 verweisen wir auf die Struktur st_Reg, und da beide Strukturen innerhalb der Struktur st_Data liegen, können wir verschiedene Arten von Informationen oder Datensätzen auf dieselbe Weise verarbeiten.
Um zu verstehen, was hier wirklich passiert, muss man üben und experimentieren. Sie werden nicht in der Lage sein, den Code zu verstehen, wenn Sie sich den Code einfach nur ansehen, wenn Sie wenig Programmiererfahrung haben. Und denken Sie daran: Wir arbeiten immer noch mit Material, das meiner Meinung nach auf einem Anfängerniveau ist.
Abschließende Überlegungen
Im heutigen Artikel haben wir gezeigt, wie man Probleme lösen kann, indem man sie strukturiert und eine einfachere und ansprechendere Lösung schafft. Obwohl es sich bei dem hier vorgestellten Material um Lehrmaterial und somit nicht um Produktionscode handelt, ist es unerlässlich, die hier vorgestellten Konzepte und Kenntnisse gründlich zu verstehen. Auf diese Weise werden Sie in Zukunft in der Lage sein, dem von uns gezeigten Code zu folgen, denn im Laufe der Zeit werden wir in der Lage sein, tiefere und interessantere Details der Programmierung zu enthüllen, was unmöglich wäre, wenn wir jede Zeile des Codes erklären müssten.
Nutzen Sie daher die Gelegenheit, das Material aus dem Index sorgfältig zu studieren, ohne sich zu beeilen. Lernen Sie und wenden Sie dieses Wissen in der Praxis an, denn im nächsten Artikel werden wir dort weitermachen, wo wir heute aufgehört haben. Wir sind noch nicht fertig mit der Diskussion über die strukturierte Programmierung und wie wir sie nutzen können, bevor wir zur objektorientierten Programmierung übergehen.
Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/15912
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Einsatz von Computer Vision im Handel mit MQL5 MQL5 (Teil 2): Erweiterung der Architektur auf die 2D-RGB-Bildanalyse
Kursbewegungen: Mathematische Modelle und technische Analyse
Eine alternative Log-datei mit der Verwendung der HTML und CSS
Auf Markov-Ketten basierendes Matrix-Prognosemodell
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.