English Русский 中文 Español 日本語 Português
preview
Von der Grundstufe bis zur Mittelstufe: Definitionen (II)

Von der Grundstufe bis zur Mittelstufe: Definitionen (II)

MetaTrader 5Beispiele |
105 0
CODE X
CODE X

Einführung

Die hier zur Verfügung gestellten Materialien sind ausschließlich für Bildungszwecke bestimmt. Sie sollte in keiner Weise als endgültige Bewerbung angesehen werden. Es geht nicht darum, die vorgestellten Konzepte zu erforschen.

Im vorigen Artikel „Von der Grundstufe zur Mittelstufe: Definitionen (I)“ besprechen wir die Kompilieranweisung #define. Wir haben gesehen, wie wir diese Richtlinie nutzen können, um unseren Code zu vereinfachen, zu beschleunigen und leichter zu implementieren, und wie wir sie kreativ und effektiv während der Sprachlernphase einsetzen können, um alles ein wenig einfacher zu machen. Um diese Ressource zu nutzen, müssen wir verstehen, was wir tun. Im Allgemeinen können wir unseren MQL5-Code mit einem exotischeren Aussehen versehen. Also gut.

Aus diesem Grund gibt es eine zweite Möglichkeit, die Kompilierungsrichtlinie #define zu verwenden. Um das, was im vorherigen Artikel besprochen wurde, nicht zu verkomplizieren, haben wir beschlossen, dies in einem separaten Artikel darzustellen. So können wir das Thema gelassener angehen. Und Sie werden in der Lage sein, in einer viel angenehmeren Form zu lernen und zu üben, was es wirklich einfacher machen wird, die Konzepte zu verstehen und zu beherrschen, da dieses Wissen von großer Bedeutung für das sein wird, was wir in den folgenden Artikeln sehen werden, vor allem, wenn wir anfangen, auf der Programmierebene zu arbeiten, die ich bereits als fortgeschritten betrachte.


Was ist ein Makro?

Um es ganz einfach auszudrücken, ist ein Makro eine kleine Prozedur, die wir mehrmals in unserem Code verwenden können. Dies ist eine sehr vereinfachte Sicht der Dinge. In den meisten Fällen (und dies gilt auch für andere, etwas komplexere Situationen) erstellen wir ein Makro, wenn ein Teil des Codes fast ständig wiederholt wird. Anstatt also denselben Code immer wieder zu schreiben, fassen wir alles in einem Stück oder einer Prozedur zusammen, die wir Makro nennen.

Diese Methode zur Definition eines Makros ist jedoch nicht wirklich geeignet. Dies ist auf mehrere Faktoren zurückzuführen, die die Erstellung einer solchen Definition zusätzlich erschweren.

Das Problem besteht darin, dass Makros größtenteils (wenn nicht sogar fast immer) so definiert sind, dass der Code inline und nicht innerhalb der Prozedur platziert wird, was dazu führen würde, dass die Aufrufe Elemente im Speicher stapeln und entstapeln. Meiner Meinung nach ist dies die beste Definition des Makros. Aber zu sagen, dass wir ein Makro verwenden, um den Code inline zu platzieren, bedeutet nicht, ihn zu etwas Besonderem zu machen. Und das liegt daran, dass wir dies theoretisch mit jeder Prozedur oder Funktion tun können. Ich sage „theoretisch“, weil ich keinen großen Unterschied in der Ausführungszeit von Funktionen oder Prozeduren feststellen konnte, die als Inline deklariert sind oder nicht.

Sie haben wahrscheinlich keine Ahnung, wovon wir sprechen. Lassen Sie mich die Situation ein wenig verdeutlichen. Wie in C und C++ gibt es auch in MQL5 ein reserviertes Wort, das ich bei anderen Programmierern, zumindest hier, selten bemerke. Dieses reservierte Wort ist inline. Aber was bedeutet dieses Wort in der Praxis? Wenn ein Programmierer einen Code erstellt, kann er normalerweise Prozeduraufrufe oder sogar Funktionsaufrufe in Inline-Code umwandeln, wenn die Sprache dies zulässt. Mit anderen Worten, wir rufen keine Prozeduren mehr auf und erhalten stattdessen den Code, der exponentiell wächst, um schneller zu laufen. Und das bedeutet, dass mehr Speicher benötigt wird.

Das mag albern oder sogar verrückt erscheinen. Bei richtiger Anwendung kann es jedoch eine gute Lösung sein, den Compiler zu zwingen, einen schnelleren Code auf Kosten von mehr Speicherplatz zu erstellen. Dabei ist jedoch Vorsicht geboten, denn wenn der Code exponentiell wächst, geraten wir früher oder später in eine Sackgasse, da wir immer mehr Speicher benötigen und die Leistung bei der Verarbeitungsgeschwindigkeit die Kosten für die Vergrößerung des Speichers nicht ausgleicht.

Lassen Sie uns als Beispiel sehen, wie dies geschehen kann. Sehen Sie sich dazu den nachstehenden Code an.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element_Default   11
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     Print("Result: ", Fibonacci_Recursive());
09.     Print("Result: ", Fibonacci_Interactive());
10. }
11. //+------------------------------------------------------------------+
12. uint Fibonacci_Recursive(uint arg = def_Fibonacci_Element_Default)
13. {
14.     if (arg <= 1)
15.         return arg;
16. 
17.     return Fibonacci_Recursive(arg - 1) +  Fibonacci_Recursive(arg - 2);
18. }
19. //+------------------------------------------------------------------+
20. inline uint Fibonacci_Interactive(uint arg = def_Fibonacci_Element_Default)
21. {
22.     uint v, i, c;
23. 
24.     for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2)
25.         v += i;
26. 
27.     return (c == arg ? i : v);
28. }
29. //+------------------------------------------------------------------+

Code 01

Bitte beachten Sie die Erklärung, denn es ist sehr wichtig, richtig zu verstehen, was ein Makro ist, wenn wir die Kompilierungsanweisung #define verwenden, um eine Routine oder Prozedur zu erstellen, die in unserem Code verwendet wird.

Beachten Sie, dass Code 01 dem, was wir bereits in anderen Artikeln gesehen haben, sehr ähnlich ist. In Zeile 20 ist jedoch etwas anderes zu finden. Dieser Unterschied ist genau das reservierte Wort inline. Jetzt, da wir wissen, wie Code 01 läuft, können wir fragen: „Aber was ist an dem einfachen Wort, das in Zeile 20 hinzugefügt wurde, das den Code anders macht?“

Die Antwort ist, dass der Code nicht so generiert wird, wie Sie es vielleicht erwarten. Das liegt daran, dass derselbe Code, wenn er in C oder C++ übersetzt wird, einen etwas anderen Code erzeugt. Es geht nicht darum, was wir sehen, sondern darum, wie der Compiler diese Art, den Code zu schreiben, behandeln wird.

Auch hier gehe ich davon aus, dass der MQL5-Compiler die Dinge so handhabt, dass es keinen merklichen Unterschied zwischen dem Code, wie er in 01 geschrieben ist, und anderem Code aus früheren Artikeln gibt. Um ehrlich zu sein, habe ich keinen Unterschied in der Verarbeitungsgeschwindigkeit festgestellt, wenn ich inline in einer Prozedur- oder Funktionsdeklaration verwende oder nicht.

In jedem Fall wird der Compiler, wenn er die Zeile 20 sieht, verstehen, dass JEDES Mal, wenn diese Fibonacci_Interactive-Funktion im Code auftaucht, der GANZE CODE zwischen den Zeilen 20 und 28 den Aufruf ersetzen muss, der als Fibonacci_Interactive existiert. Es ist jedoch notwendig, eine Datenbank mit den in der Prozedur oder Funktion vorhandenen lokalen Variablen zu erstellen, damit sie nicht mit möglichen lokalen Variablen kollidieren, die sich an der Stelle befinden, an der der Code hinzugefügt wird.

Zur Verdeutlichung: Derselbe Code 01 wird vom Compiler etwa wie folgt kompiliert:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element_Default   11
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     Print("Result: ", Fibonacci_Recursive());
09.     
10.     {
11.         uint v, i, c, arg = def_Fibonacci_Element_Default;
12. 
13.         for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2)
14.             v += i;
15. 
16.         Print("Result: ",  (c == arg ? i : v));
17. 
18.     }
19. }
20. //+------------------------------------------------------------------+
21. uint Fibonacci_Recursive(uint arg = def_Fibonacci_Element_Default)
22. {
23.     if (arg <= 1)
24.         return arg;
25. 
26.     return Fibonacci_Recursive(arg - 1) +  Fibonacci_Recursive(arg - 2);
27. }
28. //+------------------------------------------------------------------+

Code 02

Bei Code 02 handelt es sich jedoch um den Code, der erstellt und ausgeführt wird. Bitte beachten Sie jedoch, dass die Änderungen am Code auf eine ganz bestimmte Weise vorgenommen werden mussten. Und dies wird vom Compiler erledigt. Wir als Programmierer sagen dem Compiler nur, ob er eine Funktion oder Prozedur inline stellen soll oder nicht. Die Feinabstimmung liegt jedoch in der Verantwortung des Compilers.

Jetzt denken Sie vielleicht: „Okay, es gibt keinen Grund zur Sorge, denn alles scheint gleich zu sein.“ In der Tat, mein lieber Leser. Hier ist ein einfaches Beispiel. Denken Sie aber daran, dass die Funktion in Zeile 20 von Code 01 tausendmal in unserem Code verwendet werden wird. Der Compiler wird das, was in Code 02 gezeigt wird, tausendmal machen. Infolgedessen wird die ausführbare Datei immer größer, nimmt immer mehr Speicherplatz in Anspruch und benötigt immer mehr Zeit zum Laden. Selbst wenn die Ausführungsgeschwindigkeit höher wäre, könnte sie dies nicht kompensieren.

Wenn Sie dies verstanden haben, ist es sehr einfach zu verstehen, was ein Makro ist (wie wir es in Kürze definieren werden). Wir können sogar das erste Makro mit diesem Code definieren. Beachten Sie, dass zwischen den Zeilen 10 und 18 ein kompletter Code, oder besser gesagt ein Block, vom Rest des Codes isoliert ist. Um diesen Code in unser erstes Makro zu verwandeln, ändern wir ihn einfach in etwas, das wie folgt aussieht:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element_Default   11
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     Print("Result: ", Fibonacci_Recursive());
09. 
10. #define macro_Fibonacci_Interactive     {                           \
11.         uint v, i, c, arg = def_Fibonacci_Element_Default;          \
12.         for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2) v += i;  \
13.         Print("Result: ",  (c == arg ? i : v));                     \
14.                                         }
15. }
16. //+------------------------------------------------------------------+
17. uint Fibonacci_Recursive(uint arg = def_Fibonacci_Element_Default)
18. {
19.     if (arg <= 1)
20.         return arg;
21. 
22.     return Fibonacci_Recursive(arg - 1) +  Fibonacci_Recursive(arg - 2);
23. }
24. //+------------------------------------------------------------------+

Code 03

Jetzt kommt der interessanteste Teil: Code 03 ist fast, seien wir ehrlich, fast dasselbe wie Code 02. Bei der Ausführung wird das Ergebnis jedoch anders ausfallen als bei Code 02. Verstehen Sie den Unterschied, lieber Leser? Höchstwahrscheinlich nicht. Das liegt daran, dass es nicht so offensichtlich ist, wenn man sich zum ersten Mal mit dem Makro vertraut macht.

Beachten Sie nun Folgendes: Der einzige Unterschied zwischen den Codes 02 und 03 besteht im Hinzufügen der Kompilieranweisung #define in Zeile 10. Es folgt eine Konstante. Ja, das Makro IST EINE KONSTANTE, vergessen Sie das nie.

Das Makro muss, wenn es definiert ist, in EINER EINZIGEN ZEILE enthalten sein.

Es ist NICHT MÖGLICH, ein Makro zu erstellen, das mehr als eine Code-Zeile enthält. Diese Regel wird von der Programmiersprache diktiert. Damit wir also ein Makro aus mehreren Zeilen erstellen können, muss am Ende jeder Zeile ein Sonderzeichen hinzugefügt werden. Dieses Zeichen ist in jeder der Zeilen ab Zeile 10 zu sehen, aber seien Sie vorsichtig und setzen Sie diese Zeile NICHT in die letzte Zeile. Andernfalls befindet sich das Makro nicht dort, wo wir es erwarten, und Sie erhalten möglicherweise eine Fehlermeldung, wenn Sie versuchen, den Code zu kompilieren.

Es scheint alles ganz einfach zu sein, oder? Mehr oder weniger. Wenn Sie aufmerksam sind, ist das Erstellen von Makros recht einfach. Aber wenn Sie glauben, dass das alles ist, was Sie in Code 03 sehen werden, dann irren Sie sich. Dies wird ein Makro sein, das auf Code 02 basiert. Bitte beachten Sie, dass das Ergebnis bei der Ausführung von Code 03 anders ausfällt als bei Code 02. Aber warum? Der Grund dafür ist, dass das Makro NICHT VERWENDET wird. Wir geben einfach eine Erklärung dazu ab. Bei der Ausgabe von Code 02 erhalten wir also zwei Meldungen, wie Sie in der Abbildung unten sehen können.

Abbildung 01

Bei der Ausführung von Code 03 sieht die Ausgabe jedoch wie folgt aus:

Abbildung 02

Aber wie kann dieses Problem dann gelöst werden? Zu diesem Zweck müssen wir den Compiler anweisen, das Makro im Code zu verwenden. Wie Sie unten sehen können, ist dies sehr einfach.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element_Default   11
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     Print("Result: ", Fibonacci_Recursive());
09. 
10. #define macro_Fibonacci_Interactive     {                           \
11.         uint v, i, c, arg = def_Fibonacci_Element_Default;          \
12.         for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2) v += i;  \
13.         Print("Result: ",  (c == arg ? i : v));                     \
14.                                         }
15. 
16.     macro_Fibonacci_Interactive;
17. }
18. //+------------------------------------------------------------------+
19. uint Fibonacci_Recursive(uint arg = def_Fibonacci_Element_Default)
20. {
21.     if (arg <= 1)
22.         return arg;
23. 
24.     return Fibonacci_Recursive(arg - 1) +  Fibonacci_Recursive(arg - 2);
25. }
26. //+------------------------------------------------------------------+

Code 04

Das ist richtig. Wenn Sie Zeile 16 in Code 04 einfügen, erhalten Sie das gleiche Ergebnis wie in Abbildung 01, was bedeutet, dass der Code wie erwartet ausgeführt wird. Aber das war der einfachste Teil. Jetzt kommen wir zum interessantesten Teil: Diesem Thema sollten wir ein eigenes Kapitel widmen. Dies ist notwendig, weil wir zunächst verstehen müssen, was bis zu diesem Punkt gezeigt wurde, um zu verstehen, was als Nächstes getan wird.


Übergabe von Argumenten an das Makro

„Oh mein Gott! Ich dachte, wir wären fertig, und jetzt verkündest du, dass man Argumente an das Makro übergeben kann.“ Lassen Sie uns nun überprüfen, ob ich das Konzept eines Makros richtig verstanden habe. Korrigieren Sie mich, wenn ich falsch liege.

Ein Makro ist Code, der in einer Routine existiert und den wir inline in unseren Code einfügen wollen, richtig? Ganz genau. Sie haben Recht. Wenn wir also eine Möglichkeit finden, eine ganze Funktion oder Prozedur in einem Makro zu implementieren, müssen wir diese Funktion oder Prozedur nicht erstellen, da sie für den Compiler beim Kompilieren des Codes keine Rolle spielt. Habe ich Recht? Auch das ist richtig. Es gibt jedoch ein kleines Detail: Prozeduren sind einfacher zu implementieren als Funktionen, da Funktionen in den meisten Fällen eine zusätzliche Variable benötigen. In jedem Fall ist sie korrekt.

„Dann verstehe ich es. Wenn ich ein Argument an ein Makro übergeben möchte, sollte ich einfach die gleiche Deklarationsmethode wie bei der Deklaration einer Funktion oder Prozedur verwenden. Das ist ganz einfach, das ist für mich offensichtlich.“

Nun, in diesem Fall haben Sie fast recht. Das liegt daran, dass wir die Argumente, die in einem Makro übergeben werden, nicht auf dieselbe Weise deklarieren, wie es bei der Verwendung einer Funktion oder einer Prozedur der Fall ist. In diesem Fall funktionieren die Dinge ein wenig anders, und genau hier liegt die Schwierigkeit. Anders als bei der Übergabe von Parametern in Funktionen und Prozeduren, wo wir sagen können: „Dieses Argument kann nicht kontrolliert werden“, „Dieses Argument kann kontrolliert werden“, „Dieses Argument ist von diesem und jenem Typ“ und „Dieses Argument ist von diesem und jenem Typ“, haben wir bei Makros diese Art von Kontrolle NICHT. Um einige unangenehme Fehler zu vermeiden, haben wir daher keine Unterstützung durch den Compiler.

Darin liegt der Unterschied. Aus diesem Grund vermeiden viele Programmierer die Verwendung von Makros. Das liegt daran, dass jede Unachtsamkeit im Code zu ernsthaften Problemen führen kann, da Fehler in Makros sehr schwer zu beheben sind, gerade weil sie schwer zu finden sind. Der Punkt ist, dass ein Makro in einem Teil des Codes korrekte Werte erzeugen kann, aber in einem anderen Teil desselben Codes, ein wenig weiter, kann das gleiche Makro falsche Werte erzeugen. Und diese Art von Fehlern zu erkennen und zu beheben, ist meiner Meinung nach eine der schwierigsten und zermürbendsten Aufgaben.

Seien Sie also sehr vorsichtig, wenn Sie Makros in Ihrem Code verwenden: Sie sind eine wertvolle Ressource, aber sie können dazu führen, dass Sie Stunden mit der Lösung eines Problems verschwenden, das ansonsten leicht zu beheben wäre.

Da Code 04 nur eine Abwandlung von Code 01 ist, können wir, soweit wir Makros verwenden können, Code 04 abwandeln, um zu verstehen, wie man Werte innerhalb des Makros übergibt.

Bitte beachten Sie Folgendes: In Code 01, in den Zeilen 8 und 9, haben wir die Möglichkeit, zwei verschiedene Funktionen zu verwenden: eine, bei der die Antwort rekursiv ist, und eine andere, bei der die Antwort interaktiv sein wird. Aber in Code 04, obwohl wir beide Antworten haben, können wir den Wert NICHT an den interaktiven Berechnungsteil weitergeben, es sei denn, wir ändern den Wert wie in der Definition in Zeile 4 angegeben. Das ist aber nicht richtig, denn wir wollen jeden beliebigen Wert übergeben können, wie im Fall von Code 01.

Um das oben Gesagte besser zu verstehen, sehen wir uns den folgenden Code an.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element_Default   11
05. //+------------------------------------------------------------------+
06. #define macro_Fibonacci_Interactive     {                           \
07.         uint v, i, c, arg = def_Fibonacci_Element_Default;          \
08.         for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2) v += i;  \
09.         Print("Result: ",  (c == arg ? i : v));                     \
10.                                         }
11. //+----------------+
12. void OnStart(void)
13. {
14.     Print("Result: ", Fibonacci_Recursive(15));
15.     macro_Fibonacci_Interactive;
16. }
17. //+------------------------------------------------------------------+
18. uint Fibonacci_Recursive(uint arg = def_Fibonacci_Element_Default)
19. {
20.     if (arg <= 1)
21.         return arg;
22. 
23.     return Fibonacci_Recursive(arg - 1) +  Fibonacci_Recursive(arg - 2);
24. }
25. //+------------------------------------------------------------------+

Code 05

Wenn wir Code 05 ausführen, sehen wir als Ergebnis, was in der folgenden Abbildung gezeigt wird.

Abbildung 03

Offensichtlich sind die Ergebnisse unterschiedlich. Dies geschieht, weil wir in Zeile 14 von Code 05 einen Wert als Funktionsparameter in Zeile 18 übergeben. Im Makro ist dies nicht der Fall, da der Wert in der Definition von Zeile 4 festgelegt ist. Wie Sie also sehen, erfordert die Arbeit mit Makros eine gewisse Vorsicht und erhöhte Aufmerksamkeit. Um dieses Problem zu lösen, sollte man dem Makro ein Argument übergeben. Zu diesem Zweck wird der Makrocode leicht geändert. Diese Änderung ist im nachstehenden Code zu sehen:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element_Default   11
05. //+------------------------------------------------------------------+
06. #define macro_Fibonacci_Interactive(arg) {                          \
07.         uint v, i, c;                                               \
08.         for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2) v += i;  \
09.         Print("Result: ",  (c == arg ? i : v));                     \
10.                                         }
11. //+----------------+
12. void OnStart(void)
13. {
14.     Print("Result: ", Fibonacci_Recursive(15));
15.     macro_Fibonacci_Interactive(14);
16. }
17. //+------------------------------------------------------------------+
18. uint Fibonacci_Recursive(uint arg = def_Fibonacci_Element_Default)
19. {
20.     if (arg <= 1)
21.         return arg;
22. 
23.     return Fibonacci_Recursive(arg - 1) +  Fibonacci_Recursive(arg - 2);
24. }
25. //+------------------------------------------------------------------+

Code 06

Nach der Ausführung von Code 06 sieht das Ergebnis wie folgt aus:

Abbildung 04

Es ist klar, dass die Werte unterschiedlich sind, aber wir haben das absichtlich so gemacht, um zu zeigen, dass wir unabhängige Werte übergeben können und dass der eine nicht mit dem anderen zusammenhängt. Dies macht deutlich, dass ein Makro nicht mit einer anderen Funktion oder Prozedur verbunden ist.

Das war interessant, aber nicht aufregend, denn das Makro läuft weder wie eine Funktion, wie es in Code 01 der Fall wäre, noch wie eine separate Prozedur. In Wirklichkeit ist es nur ein isolierter Code. Der Code 06 läuft jedoch immer noch so, als wäre er Code 02, was in den meisten Fällen völlig nutzlos ist, da er absolut keinen anderen Zweck erfüllt, als die Zeile 15 des Codes 06 an verschiedenen anderen Stellen zu platzieren. Da diese Codes jedoch einen didaktischen Zweck haben, sind sie einfach und erfordern nicht die Verwendung eines Werkzeugs wie Makros. Wir tun dies jedoch hier, um zu erklären, wie man es verwendet und damit arbeitet.

Lassen Sie uns darüber nachdenken, wie ein Makro wie eine Funktion funktionieren kann. Tatsächlich können wir ein Makro wie das in Code 06 NICHT als Funktion verwenden. Eine Funktion ist wie eine spezielle Variable: Sie gibt immer einen Wert zurück, wenn wir sie verwenden, um einen Wert auf der Grundlage der übergebenen Argumente abzurufen. Beachten Sie, was wir gesagt haben. ICH WILL DAMIT NICHT SAGEN, dass wir Makros nicht als Funktionen verwenden können. Was ich meine, ist, dass wir dieses MACRO NICHT wie eine Funktion verwenden können. Achten Sie darauf, dass Sie Schafe und Ziegen nicht verwechseln.

Trotz dieser anfänglichen Einschränkung können wir DIESES MACRO wie eine Prozedur verhalten, in der wir einen Schritt durch Verweis machen. Wie? Es ist ganz einfach: Ändern Sie den Code wie unten gezeigt:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element_Default   11
05. //+------------------------------------------------------------------+
06. #define macro_Fibonacci_Interactive(arg) {                          \
07.         uint v, i, c;                                               \
08.         for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2) v += i;  \
09.         arg = (c == arg ? i : v);                                   \
10.                                         }
11. //+----------------+
12. void OnStart(void)
13. {
14.     ulong value;
15. 
16.     Print("Result: ", Fibonacci_Recursive((uint)(value = 15)));
17.     macro_Fibonacci_Interactive(value);
18.     Print("Checking: ", value);
19. }
20. //+------------------------------------------------------------------+
21. uint Fibonacci_Recursive(uint arg = def_Fibonacci_Element_Default)
22. {
23.     if (arg <= 1)
24.         return arg;
25. 
26.     return Fibonacci_Recursive(arg - 1) +  Fibonacci_Recursive(arg - 2);
27. }
28. //+------------------------------------------------------------------+

Code 07

Jetzt wird es interessant. Die Sache ist die, dass wir angefangen haben, das Makro, sagen wir, für edlere Zwecke zu verwenden. Beachten Sie Folgendes: In Zeile 14 deklarieren wir eine Variable, aber WIR INITIALISIEREN SIE NICHT. Wir initialisieren ihn in Zeile 16. Sie fragen sich vielleicht: „Was zum Teufel ist in Zeile 16 los?“ Keine Sorge, das ist kein Wahnsinn.

Bitte beachten Sie, dass die Variable in Zeile 14 vom Typ ulong ist, während der von der Funktion in Zeile 21 erwartete Typ vom Typ uint ist. Da wir sowohl für die Funktion in Zeile 21 als auch für das Makro, das weiter unten verwendet wird, denselben Wert verwenden wollen, sollten wir eine explizite Typumwandlung vornehmen, damit der Compiler keine Warnungen ausgibt. Aus diesem Grund sieht die Erklärung in Zeile 16 wie folgt aus.

Okay, gleich danach haben wir Zeile 17. Hier beginnt der Spaß. Dies geschieht, weil wir die Variable per Referenz übergeben. Wenn also das Makro den Wert der Variablen ändert, wird diese Änderung hier in unserem Hauptcode wiedergegeben. Der Wert ändert sich in Zeile 9, man sollte also wachsam sein. Sonst könnten wir eine Zeitbombe bekommen. Und um zu demonstrieren, dass dieses System funktioniert und verwendet werden kann, haben wir Zeile 18, in der der Wert der Variablen auf dem Terminal ausgegeben wird. Nach der Ausführung von Code 07 erhalten wir das unten gezeigte Bild:

Abbildung 05

Nun gut, ich denke, es ist klar, dass wir jedes Mal, wenn wir eine Variable an ein Makro übergeben, dies durch Anklicken des Links tun. Deshalb müssen wir darauf achten, dass das Makro den Wert nicht fälschlicherweise ändert. Aber warten Sie einen Moment. Die Art von Makro, die wir in Code 07 sehen, ist ein Makro, das als Prozedur läuft. Gibt es eine Möglichkeit, ein Makro als Funktion zu verwenden? Das heißt, wir können einen Wert senden und einen anderen als Antwort erhalten. Diese Frage stellen sich viele Programmierer, wenn sie mit dem Erlernen von Makros beginnen.

Grundsätzlich sind Makros eher prozedurorientiert, aber je nachdem, wie wir den Code innerhalb des Makros aufbauen, können wir ihn so laufen lassen, als wäre er eine Funktion. In diesem Fall behandeln wir die Codes in diesen Artikeln vorgestellt, können wir ein Beispiel für diese, nur als eine Art der Darstellung des Mechanismus zu schaffen. Auch wenn es nicht sehr gut durchdacht ist und vielleicht nicht sehr interessant erscheint, lohnt es sich, es zu versuchen. Ein Beispiel hierfür ist im folgenden Code zu sehen:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element_Default   11
05. //+------------------------------------------------------------------+
06. #define macro_Ternário(A, B, C, D)  (A == B ? C : D)
07. //+----------------+
08. #define macro_Fibonacci_Interactive(arg) {                          \
09.         uint v, i, c;                                               \
10.         for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2) v += i;  \
11.         arg = macro_Ternário(c, arg, i, v);                         \
12.                                         }
13. //+----------------+
14. void OnStart(void)
15. {
16.     ulong value;
17. 
18.     Print("Result: ", Fibonacci_Recursive((uint)(value = 15)));
19.     macro_Fibonacci_Interactive(value);
20.     Print("Checking: ", value);
21. }
22. //+------------------------------------------------------------------+
23. uint Fibonacci_Recursive(uint arg = def_Fibonacci_Element_Default)
24. {
25.     if (arg <= 1)
26.         return arg;
27. 
28.     return Fibonacci_Recursive(arg - 1) +  Fibonacci_Recursive(arg - 2);
29. }
30. //+------------------------------------------------------------------+

Code 08

Hier, in Code 08, sehen wir ein kleines Demo eines Makros, das als Funktion läuft. Dieses Makro, definiert in Zeile sechs, ist ein einfaches Beispiel dafür, wie ein Makro als Funktion ausgeführt werden kann. Beachten Sie, dass wir es in Zeile 11 verwenden. Im Wesentlichen können Sie damit einige der Komplexitäten im Code verbergen, denn je nach dem Namen, den wir dem Makro geben, ist es viel einfacher zu verstehen, wie es funktioniert.

Bitte beachten Sie, dass es hier NICHT darum geht, den Code effizienter zu machen, sondern darum, ihn lesbarer zu machen. Sie können zum Beispiel eine Reihe von Makros zur Bearbeitung von Datums- und Zeitwerten erstellen, die den Typ datetime verwenden. Dies ist ein typisches Beispiel für Makros mit der Funktionalität einer Funktion. Um dies anschaulicher zu machen, erstellen wir einige Makros, um zu zeigen, worüber wir sprechen.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. enum eConvert {
05.     FORMAT_DECIMAL,
06.     FORMAT_OCTAL,
07.     FORMAT_HEX,
08.     FORMAT_BINARY
09. };
10. //+------------------------------------------------------------------+
11. #define macro_GetDate(A)            (A - (A % 86400))
12. #define macro_GetTime(A)            (A % 86400)
13. //+----------------+
14. #define macro_GetSec(A)             (A - (A - (A % 60)))
15. #define macro_GetMin(A)             (int)((A - (A - ((A % 3600) - (A % 60)))) / 60)
16. #define macro_GetHour(A)            (int)((A - (A - ((A % 86400) - (A % 3600)))) / 3600)
17. //+----------------+
18. #define PrintX(X)                   Print(#X, " => ", X);
19. //+------------------------------------------------------------------+
                   .
                   .
                   .

Snippet 01

In Snippet 01 haben wir unsere Include-Datei. Ich füge Makros ein, um zu zeigen, dass wir mit der Zeit unsere Programmierfähigkeiten erheblich erweitern können, wenn wir Erfahrung sammeln und eine spezielle Bibliothek aufbauen.

Um zu testen, was diese Makros zwischen den Zeilen 11 und 18 bewirken, verwenden wir einen Testcode. Sie finden sie weiter unten:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "Tutorial\File 01.mqh"
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     datetime dt = D'2024.08.15 12:30:27';
09.     ulong v;
10. 
11.     Print("Date And Time: ", dt);
12.     Print("0x", ValueToString(dt, FORMAT_HEX));
13.     PrintX((datetime)(v = macro_GetDate(dt)));
14.     Print("0x", ValueToString(v, FORMAT_HEX), " :: ", ValueToString(v, FORMAT_DECIMAL));
15.     PrintX((datetime)(v = macro_GetTime(dt)));
16.     Print("0x", ValueToString(v, FORMAT_HEX), " :: ", ValueToString(v, FORMAT_DECIMAL));
17.     PrintX((datetime)(v = macro_GetSec(dt)));
18.     Print("0x", ValueToString(v, FORMAT_HEX), " :: ", ValueToString(v, FORMAT_DECIMAL));
19.     PrintX((datetime)(v = macro_GetMin(dt)));
20.     Print("0x", ValueToString(v, FORMAT_HEX), " :: ", ValueToString(v, FORMAT_DECIMAL));
21.     PrintX((datetime)(v = macro_GetHour(dt)));
22.     Print("0x", ValueToString(v, FORMAT_HEX), " :: ", ValueToString(v, FORMAT_DECIMAL));
23. }
24. //+------------------------------------------------------------------+

Code 09

Das ist sehr interessant und aufregend, weil wir ohne großen Aufwand eine Reihe von Dingen analysieren können, die sonst mit Hilfe anderer, noch nicht erläuterter Ressourcen durchgeführt werden müssten. Wenn wir Code 09 ausführen, wird folgendes angezeigt:

Abbildung 06

Jetzt werden Sie verstehen, warum ich gesagt habe, dass Code 09 recht interessant ist. In den Zeilen 13, 15, 17, 19 und 21 haben wir Aufrufe des Makros. Dieses Makro ist in Zeile 18 von Snippet 01 definiert. Der Zweck des Makros ist es, uns den Namen der Variablen und ihren Wert mitzuteilen. In diesem Fall ist der Variablenname eine Funktion, die in jeder der in Code 09 genannten Zeilen unterschiedliche Makros verwendet. Dies ist eine recht ungewöhnliche und interessante Kombination, da der von jedem Makro zurückgegebene Wert in einer lokalen Variablen gespeichert wird und unmittelbar danach der hexadezimale Wert dieser Variablen sowie ihr dezimaler Wert ausgegeben wird.

Dies zeigt, dass wir tatsächlich die richtigen Werte im Datetime-Format erfassen. Ich weiß, dass viele von Ihnen das für Unsinn halten, aber wie Sie sehen können, ist dieses Makro sehr nützlich, vor allem, wenn wir wollen, dass der Code so schnell und sicher wie möglich läuft. Und es macht die ganze Arbeit beim Schreiben des Codes viel einfacher, angenehmer und lustiger.


Abschließende Überlegungen

Dieser Artikel hat sich mit der Frage befasst, was Makros sind und wie sie für die Codierung zu behandeln sind, da sie eine der Ressourcen sind, die oft missbraucht und in anderen Fällen völlig ignoriert werden, gerade wegen des Mangels an Wissen und Praxis bei ihrer Verwendung. Viele Programmierer verstehen am Ende nicht, warum etwas, das sie für unmöglich (oder sogar unerreichbar) hielten, von einem anderen, oft unbekannten Programmierer erledigt werden kann.

Ich selbst vertrete (und das glaube ich gerne) die folgende These:

Es gibt keine schlechten Programmierer, es gibt nur solche, die ihren Weg nicht gefunden haben. Ja, es gibt sowohl unausgebildete Fachleute als auch Fachleute, die sich für vorbereitet halten, aber in Wirklichkeit sind sie noch neu im Programmieren.

Das Konzept, das ich Ihnen in diesen Artikeln zu vermitteln versuche, ist speziell für diejenigen gedacht, die gerade erst anfangen, das Programmieren zu lernen. Wenn Sie den richtigen Weg einschlagen und die Konzepte und Gründe für die Existenz eines bestimmten Instruments richtig verstehen, wird es Ihnen leichter fallen, Ihre Ideen zu verwirklichen. Sie, liebe Leserinnen und Leser, werden dieses Material vielleicht etwas albern und ohne einen bestimmten Zweck oder eine bestimmte Aufgabe finden, aber wenn Sie das, was hier besprochen wurde, in die Praxis umsetzen und anwenden, werden Sie feststellen, dass viele der Dinge, die Ihnen zuvor schwierig und zäh erschienen, nun durchaus erreichbar sind. Natürlich gibt es noch einige Dinge zu klären, aber wir haben bereits viele Schritte in die richtige Richtung unternommen.

Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/15588

Beigefügte Dateien |
Anexo.zip (4.58 KB)
Entwicklung eines Replay-Systems (Teil 78): Neuer Chart Trade (V) Entwicklung eines Replay-Systems (Teil 78): Neuer Chart Trade (V)
In diesem Artikel werden wir uns ansehen, wie ein Teil des Empfängercodes implementiert wird. Hier werden wir einen Expert Advisor implementieren, um zu testen und zu lernen, wie die Interaktion mit dem Protokoll funktioniert. Der hier dargestellte Inhalt ist ausschließlich für Bildungszwecke bestimmt. Die Anwendung sollte unter keinen Umständen zu einem anderen Zweck als zum Erlernen und Beherrschen der vorgestellten Konzepte verwendet werden.
Entwicklung eines Replay-Systems (Teil 77): Neuer Chart Trade (IV) Entwicklung eines Replay-Systems (Teil 77): Neuer Chart Trade (IV)
In diesem Artikel werden wir einige der Maßnahmen und Vorsichtsmaßnahmen behandeln, die bei der Erstellung eines Kommunikationsprotokolls zu beachten sind. Dies sind recht einfache und unkomplizierte Dinge, sodass wir in diesem Artikel nicht zu sehr ins Detail gehen werden. Aber um zu verstehen, was passieren wird, müssen Sie den Inhalt des Artikels verstehen.
Von der Grundstufe bis zur Mittelstufe: Template und Typenname (III) Von der Grundstufe bis zur Mittelstufe: Template und Typenname (III)
In diesem Artikel werden wir den ersten Teil des Themas behandeln, der für Anfänger nicht so leicht zu verstehen ist. Um nicht noch mehr Verwirrung zu stiften und dieses Thema richtig zu erklären, werden wir die Erklärung in Etappen unterteilen. Dieser Artikel ist der ersten Phase gewidmet. Auch wenn es am Ende des Artikels so aussehen mag, als hätten wir eine Sackgasse erreicht, werden wir in Wirklichkeit einen Schritt in Richtung einer anderen Situation machen, die im nächsten Artikel besser verstanden wird.
Implementierung von praktischen Modulen aus anderen Sprachen in MQL5 (Teil 02): Aufbau der REQUESTS-Bibliothek, inspiriert von Python Implementierung von praktischen Modulen aus anderen Sprachen in MQL5 (Teil 02): Aufbau der REQUESTS-Bibliothek, inspiriert von Python
In diesem Artikel implementieren wir ein Modul, das den in Python angebotenen Anfragen ähnelt, um das Senden und Empfangen von Web-Anfragen in MetaTrader 5 mit MQL5 zu erleichtern.