
Von der Grundstufe bis zur Mittelstufe: Prioritätsfolge der Operatoren
Einführung
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.
Im vorherigen Artikel „Von der Grundstufe zur Mittelstufe: Die Anweisung FOR“, haben wir die Grundlagen der FOR-Anweisung behandelt. Mit dem Material, das in früheren Artikeln vorgestellt wurde, können Sie eine ganze Menge MQL5-Code erstellen. Auch wenn es sich um recht einfache Anwendungen handelt, können sie für viele eine Quelle des Stolzes und der Freude sein.
Für viele andere Programmierer können kleine Codeschnipsel, die von Anfängern erstellt werden, trivial erscheinen, aber wenn der Anfänger eine Lösung für ein Problem findet, ist das wirklich ein Grund, stolz zu sein. Um ehrlich zu sein, können Sie mit dem, was wir bisher besprochen haben, nur einen skriptähnlichen Code erstellen. Selbst wenn es sich um etwas sehr Einfaches handelt, das keine Interaktion erfordert, haben Sie, wenn Sie es selbst gemacht haben, damit begonnen, die Grundkenntnisse anzuwenden und sich in die richtige Richtung zu bewegen.
Aber jetzt ist es an der Zeit, sich einem neuen Thema zuzuwenden. Dieses Thema wird es uns ermöglichen, bei der Erstellung eines sinnvolleren Codes weiter voranzukommen. Heute werden wir uns mit Operatoren beschäftigen. Obwohl wir schon früher darüber gesprochen haben, werden wir jetzt ein paar Schritte weiter gehen, denn was wir zuvor behandelt haben, war recht grundlegend und einfach. In diesem Artikel werden wir uns mit den Regeln für die Prioritätsfolge von Operatoren in der Praxis befassen sowie mit dem ternären Operator, der für viele ein etwas verwirrendes Konzept darstellt, aber in einer Vielzahl von Situationen äußerst nützlich ist. Sie kann uns bei bestimmten Programmieraufgaben sowohl Zeit als auch Mühe ersparen.
Die Voraussetzung, um den Inhalt dieses Artikels zu verstehen, ist ein Verständnis dafür, wie man Variablen in MQL5-Code deklariert und verwendet. Dieses Thema ist bereits in früheren Artikeln behandelt worden. Wenn Sie noch nicht über diese Kenntnisse verfügen, lesen Sie bitte die früheren Artikel, bevor Sie mit diesem Artikel fortfahren. Lassen Sie uns nun mit dem ersten Thema dieses Artikels beginnen.
Prioritätsregeln
Es ist äußerst wichtig, die Prioritätsregeln zu verstehen und mit ihnen vertraut zu sein, und ich habe dies bereits in einem anderen Artikel erwähnt. Aber hier werden wir ein bisschen tiefer in dieses Thema eintauchen.
Wenn Sie die Dokumentation zu den Prioritätsregeln konsultieren, werden Sie eine Tabelle finden, in der sie aufgeführt sind. Viele Menschen verstehen jedoch die Bedeutung oder die in den Vorschriften enthaltenen Informationen nicht richtig. Wenn das bei Ihnen der Fall ist, liebe Leserin, lieber Leser, brauchen Sie sich nicht zu schämen oder zu zögern. Es ist völlig normal, dass man etwas verwirrt ist, wenn man zum ersten Mal mit dieser Art von Informationen konfrontiert wird. Schließlich lernen wir in der Schule die Dinge in der Regel nicht auf die gleiche Weise, wie wir sie hier, als Programmierer, anwenden.
Obwohl die Art und Weise, wie bestimmte Ausdrücke von einigen Programmierern strukturiert werden, auf den ersten Blick verwirrend erscheinen mag, ist sie technisch oft korrekt. Auch wenn das Ergebnis für uns unerwartet erscheint. Deshalb ist es so wichtig, die Prioritätsregeln zu verstehen. Sie brauchen sie nicht auswendig zu lernen. Bei regelmäßiger Anwendung und Übung werden Sie sich daran gewöhnen. Aber das Wichtigste ist:
Wenn Sie Ihren eigenen Code nicht verstehen, werden es andere sicher auch nicht.
Deshalb sollten Sie immer versuchen, sich zu überlegen, wie andere das, was Sie zu strukturieren versuchen, interpretieren könnten. Beginnen wir also mit dem folgenden Punkt: Die zu Beginn dieses Themas erwähnte Tabelle sollte von oben nach unten gelesen werden. Die zuoberst aufgeführten Operatoren haben eine höhere Ausführungspriorität. Wenn Sie sich in der Tabelle nach unten bewegen, nimmt die Priorität schrittweise ab.
Es gibt jedoch ein kleines Detail, das zu beachten ist. Um dies zu erklären, sehen wir uns die Tabelle in der folgenden Abbildung an:
Abbildung 01
Hier sehen Sie, dass die Operatoren in einer bestimmten Reihenfolge aufgelistet sind. Die Einhaltung dieser Reihenfolge ist sehr wichtig. Sie werden auch feststellen, dass sie in Kategorien gruppiert sind. Dies ist der Punkt, der viele Menschen verwirren könnte. Das liegt daran, dass wir, wenn wir auf bestimmte Codestücke stoßen, das Ergebnis des Ausdrucks nicht vorhersagen können, wenn wir die in Abbildung 01 gezeigte Gruppierung nicht verstehen. Glücklicherweise ist diese Gruppierung recht einfach zu verstehen und erleichtert die Arbeit, sodass Sie nicht alle Prioritätsregeln auswendig lernen müssen. Zunächst haben wir die Referenzierungsoperatoren. Diese haben Prioritätsfolge vor allen anderen, da sie bestimmen, wie wir auf ein bestimmtes Element zugreifen. Gleich danach kommen Typ- oder Binäroperatoren. In diesen Fällen sollte der Code von rechts nach links gelesen werden. Aber warum ist das so? Keine Sorge, dazu kommen wir noch. Ich werde genau erklären, wie der Code in solchen Situationen zu lesen ist. Beachten Sie vorerst nur, dass dies ein Unterschied zu referenzierenden Operatoren ist, die von links nach rechts gelesen werden.
Ich weiß, das klingt verwirrend. An dieser Stelle werden Sie vielleicht denken: „Warum machen sie es so kompliziert?“ Aber das ist keine Komplikation um der Komplikation willen, lieber Leser. In der Praxis macht das alles durchaus Sinn. Ohne es in Aktion zu sehen, könnte man meinen, man sei in einem Irrenhaus gelandet. In der dritten Gruppe, die Elemente enthält, die den meisten Menschen vertraut sind, werden die Dinge jedoch klarer. Dies sind die grundlegenden arithmetischen Operatoren, und in diesem Fall wird der Code von links nach rechts gelesen. Dieses Muster setzt sich im Rest von Abbildung 01 fort.
Schauen wir uns nun an, wie all dies in der Praxis funktioniert. Dazu werden wir einige sehr einfache und unkomplizierte Codeschnipsel verwenden, in denen wir einfach einige Werte ausgeben. Das ist der lustige und einfache Teil.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define PrintX(X) Print("Factoring: { ", #X, " } is: ", X) 05. //+------------------------------------------------------------------+ 06. void OnStart(void) 07. { 08. char value = 9; 09. 10. PrintX(value); 11. PrintX(++value * 5); 12. PrintX(value); 13. } 14. //+------------------------------------------------------------------+
Code 01
Nun frage ich Sie, liebe Leserin, lieber Leser: Welcher Wert wird im Terminal ausgedruckt? Ohne ein klares Verständnis der Prioritätsfolge von Operatoren könnte man auf 46 tippen, da wir eine Variable (mit einem Wert von neun) mit einer Konstanten (Wert fünf) multiplizieren und dann eins hinzufügen. Aber das wäre FALSCH. Das tatsächliche Ergebnis der Ausführung von Code 01 ist 50, wie in der folgenden Abbildung dargestellt:
Abbildung 02
Hat es Sie verwirrt? Wie wäre es dann mit einem weiteren kleinen Experiment, bei dem wir diesmal nur ein kleines Detail im Code ändern. Sie können es gleich unten sehen:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define PrintX(X) Print("Factoring: { ", #X, " } is: ", X) 05. //+------------------------------------------------------------------+ 06. void OnStart(void) 07. { 08. char value = 9; 09. 10. PrintX(value); 11. PrintX(value++ * 5); 12. PrintX(value); 13. } 14. //+------------------------------------------------------------------+
Code 02
Das Ergebnis von Code 02 ist unten dargestellt:
Abbildung 03
Habe ich nicht im letzten Artikel gesagt, dass wir viel Spaß haben werden? Ich könnte noch eine ganze Weile mit diesen Präzedenzregeln herumspielen, nur um Ihnen, liebe Leserin, lieber Leser, zu zeigen, dass Sie in dem Moment, in dem Sie glauben, alles verstanden zu haben und bereit zu sein, die Welt zu erobern, in Wirklichkeit gerade erst aufgestanden sind ... und dabei sind, Ihren allerersten Schritt zu tun.
Ich weiß, Sie brauchen mir nicht zu sagen, dass das alles wie Wahnsinn aussieht. Und Sie denken wahrscheinlich, dass ich ein Verrückter bin. Aber glauben Sie mir, lieber Leser, der Spaß hat gerade erst begonnen. Die Dinge werden jetzt noch interessanter werden. Wie wäre es, wenn wir uns ein noch unterhaltsameres Stück Code ansehen? Beginnen wir mit dem unten abgebildeten Beispiel:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define PrintX(X) Print("Factoring: { ", #X, " } is: ", X) 05. //+------------------------------------------------------------------+ 06. void OnStart(void) 07. { 08. char v1 = 9, 09. v2 = 5; 10. 11. PrintX(v1); 12. PrintX(v2); 13. PrintX(v1 & 1 * v2); 14. PrintX((v1 & 1) * v2); 15. } 16. //+------------------------------------------------------------------+
Code 03
Schöner Code. Er ist wirklich reizvoll, besonders wenn er in einer Schleife läuft. Nachfolgend sehen Sie die Ausgabe, die das Programm erzeugt.
Abbildung 04
Geben Sie es zu: Sie haben, genau wie ich, Spaß daran, so etwas zu sehen, nicht wahr? Beachten Sie, dass das Ergebnis vom Vorhandensein oder Fehlen von Klammern in einem Ausdruck abhängt.
Es gibt eigentlich eine allgemeine Regel, wenn es darum geht, diese Art von Ausdruck zu schreiben. Es ist keine strenge oder offiziell dokumentierte Regel, aber sie existiert unter Programmierern. Und die Regel lautet wie folgt:
Wenn Sie einen komplexen Ausdruck schreiben, versuchen Sie, ihn mit Hilfe von Klammern in verschiedene Ausführungsebenen zu unterteilen. Das macht es für andere Programmierer einfacher zu interpretieren.
Selbst wenn das Ergebnis offensichtlich ist und der Prioritätsfolge der Standardoperatoren folgt, ist es viel einfacher zu verstehen, welche Art von Ergebnis Sie erwarten, wenn Sie die Dinge mit Hilfe von Klammern in klar definierte Ebenen unterteilen. In manchen Fällen kann nicht einmal der Compiler richtig interpretieren, was Sie zu tun versuchen. Möchten Sie ein Beispiel dafür sehen? Dann werfen Sie einen Blick auf den unten stehenden Code. Dies ist ein Beispiel für ein Stück Code, dessen Ergebnis völlig unvorhersehbar ist, da selbst der Compiler Schwierigkeiten hat, zu verstehen, was getan werden soll.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define PrintX(X) Print("Factoring: { ", #X, " } is: ", X) 05. //+------------------------------------------------------------------+ 06. void OnStart(void) 07. { 08. char v1 = 9, 09. v2 = 5; 10. 11. PrintX(v1); 12. PrintX(v2); 13. PrintX(v1++ & 1 * v2 << 1); 14. PrintX(v1); 15. PrintX(v2); 16. } 17. //+------------------------------------------------------------------+
Code 04
Wenn Sie in diesem Fall versuchen, Code 04 zu kompilieren, gibt der Compiler eine Warnung aus (siehe unten):
Abbildung 05
Beachten Sie, dass der Compiler trotz der Warnung den Code erzeugt. Allerdings besteht hier ein erhebliches Risiko, dass der Code in bestimmten Situationen nicht das richtige Ergebnis liefert. Daher wäre es unklug, diesem Code blind zu vertrauen, insbesondere wenn der Compiler eine Warnung ausgibt, die auf einen möglichen Fehler im Ausdruck hinweist. In solchen Situationen ist es notwendig, Klammern zu verwenden. Ohne sie hinzuzufügen und nur mit den Regeln für die Prioritätsfolge der Operatoren können wir sehen, ob das Ergebnis in diesem speziellen Fall immer noch korrekt wäre.
Dazu führen wir den Code aus, und das Ergebnis ist unmittelbar darunter zu sehen:
Abbildung 06
Hier wird der Wert acht ausgegeben. Aber ist dieser Wert tatsächlich korrekt? Um das festzustellen, müssen wir manuell aufschlüsseln, wie der Ausdruck vom Code verarbeitet wurde. Diese Art der Analyse ist in der Programmierung sehr verbreitet. Im Gegensatz zu dem, was manche denken mögen, schreibt ein Programmierer nicht einfach Code, ohne zu wissen, was das Ergebnis sein soll. Ein guter Programmierer weiß IMMER, welches Ergebnis sein Code liefern soll. Schreiben Sie NIEMALS etwas, ohne vorher das beabsichtigte Ergebnis zu kennen. Ein guter Programmierer führt im Grunde genommen einen Backtest seiner eigenen Logik durch und führt dann einen so genannten Vorwärtstest durch, bei dem jedes Ergebnis einzeln überprüft wird. Erst nach einer gründlichen Testrunde fangen sie an, ihrem Code zu vertrauen, wenn auch nie ganz. Ein guter Entwickler bewahrt sich immer ein gesundes Maß an Skepsis. Aber der Grund für diese Skepsis wird in einem anderen Artikel behandelt. Lassen Sie uns zunächst prüfen, ob das Ergebnis von acht für Code 04 tatsächlich korrekt ist.
Um dies zu überprüfen, müssen wir verstehen, wie der Compiler das, was er berechnen soll, interpretiert. Da der Compiler strenge Prioritätsregeln befolgt, die in der Tabelle der Prioritätsregeln für Operatoren festgelegt sind, können wir die Berechnung in Zeile 13 manuell faktorisieren und überprüfen, ob sie korrekt ist.
Wir beginnen mit der Identifizierung des Operators mit der höchsten Priorität. In diesem Fall ist es der Operator ++, der auf die Variable v1 angewendet wird. Dieser Operator wird jedoch von rechts nach links ausgewertet. In diesem Fall wird also die Prioritätsfolge geändert und die Auswirkung auf die Variable v1 angewandt, nachdem alle Operationen mit höherem Prioritätsfolge zu ihrer Rechten abgeschlossen worden sind. In unserem Fall ist ein solcher Operator der Multiplikationsoperator, der Vorrang vor dem Linksschiebeoperator und dem UND-Operator hat. Die erste Operation ist also die Multiplikation von 1 mit v2, was 5 ergibt. Anschließend wird der Linksverschiebungsoperator angewendet, um den Wert 5 um ein Bit nach links zu verschieben. Dadurch wird ein neuer Wert von 10 erzeugt. Dann führen wir das bitweise UND zwischen v1 und 10 aus. Da v1 gleich neun ist, ist das Ergebnis der UND-Verknüpfung acht. Schließlich, nachdem alle Operationen auf der rechten Seite ausgeführt wurden, wird der Wert von v1 um eins erhöht. Wenn wir also faktorisieren, erhalten wir einen Wert von acht, und wenn wir die Faktorisierung abschließen, erhalten wir einen Wert von zehn für v1.
Sie werden jetzt vielleicht denken: „Ah, jetzt verstehe ich. Das ist eigentlich ganz einfach und klar“. Aber ist es das wirklich, lieber Leser? Sind Sie sicher, dass Sie wirklich verstanden haben, wie dieser Prozess funktioniert? Finden wir es heraus. Wie wäre es, wenn Sie versuchen würden, den Ausdruck im nächsten Codeschnipsel unten zu lösen?
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define PrintX(X) Print("Factoring: { ", #X, " } is: ", X) 05. //+------------------------------------------------------------------+ 06. void OnStart(void) 07. { 08. char v1 = 9, 09. v2 = 5; 10. 11. PrintX(v1); 12. PrintX(v2); 13. PrintX(++v1 & 1 * v2 << 1); 14. PrintX(v1); 15. PrintX(v2); 16. } 17. //+------------------------------------------------------------------+
Code 05
Beantworten Sie diese Frage ohne Zögern. Wenn Sie diese Frage wirklich richtig beantworten können, dann haben Sie wirklich verstanden, wie die Prioritätsfolge funktioniert. Aber auch so, nur um Sie ein wenig zu verwirren, werde ich Ihnen ein Ergebnis zeigen, und ich möchte, dass Sie mir sagen, ob es richtig oder falsch ist.
Wenn ich diesen Code ausführe, erhalte ich das unten dargestellte Ergebnis:
Abbildung 07
Nun sagen Sie mir: Warum ist mein Ergebnis zehn und nicht acht? Dafür gibt es einen Grund, der in den Prioritätsregeln liegt. Nun, liebe Leserin, lieber Leser, da ich mich nicht gerne mit anderen Leuten anlege, wollen wir uns einmal ansehen, warum diese Ergebnisse so unterschiedlich sind. Aber dafür brauchen wir ein neues Thema. Ich möchte niemandem mit einem einzigen Artikel das Gehirn braten. Wir haben hier noch ein weiteres wichtiges Thema zu behandeln.
BackTest und ForwardTest
Das erste, was Sie verstehen müssen, liebe Leserin, lieber Leser, ist Folgendes: Bevor Sie einen Code schreiben, sollten Sie bereits wissen, welches Ergebnis er bringen wird. Beim Programmieren geht es nicht darum, Code zu schreiben, um eine unbekannte Antwort zu erhalten. Es ist genau das Gegenteil. Sie schreiben Code, um ein Ergebnis zu erzielen, das Sie bereits kennen. Und in diesem Fall, egal ob es sich um Code 04 oder Code 05 handelt, wusste ich bereits, was das Ergebnis sein sollte. Obwohl ich wusste, dass sie aufgrund der Compiler-Warnung falsch sein könnte, hatte ich trotzdem eine Vorhersage. Wie kann ich das Ergebnis kennen, bevor ich den Code schreibe? Es scheint sinnlos zu sein. Nun ja, viele Leute sagen, man solle erst programmieren und dann das Ergebnis sehen. Aber wenn man die Rangfolge kennt, weiß man das Ergebnis schon, bevor es überhaupt gedruckt wird.
Um dies zu verdeutlichen, gehen wir wie folgt vor: Wie in den angehängten Dateien gezeigt, stelle ich diese Code-Beispiele zur Verfügung, damit Sie sie selbst ausführen können und genau sehen, was ich hier zeige. Auf diese Weise werden Sie wirklich verstehen, warum es so wichtig ist, die Antwort schon im Voraus zu kennen, noch bevor Sie den Code schreiben, der Ihnen die Antwort liefert.
Nun zu der eigentlichen Frage. Welche Antwort ist richtig: die aus Abbildung 06 oder die aus Abbildung 07? Und wenn ich Ihnen sage, dass beides richtig ist? Was würden Sie sagen? Wahrscheinlich, dass ich den Verstand verloren habe. Denn wie kann ein und derselbe Ausdruck zwei verschiedene Ergebnisse liefern und beide richtig sein? So verrückt es auch klingen mag, beide Antworten sind richtig. Es ist jedoch sehr viel wahrscheinlicher, dass das erwartete Ergebnis das in Abbildung 07 gezeigte ist. Das Ergebnis in Abbildung 06 ist hingegen ein Nebeneffekt, der durch eine falsche Anwendung oder ein Missverständnis der Rangfolge verursacht wird. Wenn wir in Code 04 Klammern verwenden, um die Reihenfolge der Operatoren explizit festzulegen, entspricht das Ergebnis dem in Abbildung 07 gezeigten. Und es braucht nicht viel: nur eine kleine Änderung, damit der Inkrement-Operator (++) mit höherer Priorität ausgeführt wird. In Code 05 hat der Inkrement-Operator ist vor der Variablen platziert, da er Vorrang vor der Multiplikation hat. Aber wie bereits erläutert, steht der Operator in Code 04 nach der Variablen, d. h. er wird erst nach dem Rest des Ausdrucks ausgeführt.
Sie glauben mir nicht? Wie wäre es dann, wenn wir es testen? Ändern wir die Zeile 13 von Code 04 in die unten gezeigte Zeile:
PrintX((v1++ & 1) * (v2 << 1));
Und mit dieser Änderung warnt der Compiler nicht mehr, dass das Ergebnis unzuverlässig sein könnte. Nachdem derselbe Code 04 diese einfache Änderung erfahren hat, erhalten wir nun das in Abbildung 07 dargestellte Ergebnis. Genau das ist es, was bestimmte Aspekte der Programmierung so kompliziert erscheinen lässt. Viele Menschen studieren das Programmieren aus den falschen Gründen. In Wirklichkeit besteht der eigentliche Zweck der Programmierung darin, den Computer dazu zu bringen, uns ein bekanntes Ergebnis schneller zu liefern. Wenn wir versuchen, eine Antwort zu finden, die wir noch nicht kennen, können wir die Antwort des Computers als einen gültigen Hinweis betrachten. Aber es gibt eine Grenze, wie sehr wir uns darauf verlassen können. Innerhalb dieser Grenzen können Sie jedoch, sobald Ihr Code getestet wurde, sowohl mit bekannten Werten als auch mit Werten, die Sie manuell überprüfen können, endlich sagen: „MEIN PROGRAMM KANN DIES ODER JENES BERECHNEN“. Bis dahin ist Ihr Code wahrscheinlich fehlerhaft, weil er nicht richtig getestet wurde.
Nun gut, ich überlasse es Ihnen, dies in Ruhe zu studieren und darüber nachzudenken, was wir behandelt haben. Doch bevor wir diesen Artikel abschließen, möchte ich noch auf einen letzten Punkt eingehen. Noch ein letzter Operator, den wir noch nicht besprochen haben. Kommen wir nun zum letzten Thema.
Der ternäre Operator
Dies ist mein Lieblingsoperator. Jeder, der meine Artikel und hier in der Community verfolgt, ist es wahrscheinlich leid, zu sehen, wie oft ich es verwende. Warum ist es also mein Favorit? Ganz einfach: Es erlaubt mir, eine Logik aufzubauen, ohne die IF-Anweisung zu benötigen. IF ist ein grundlegender Bestandteil der Programmierung, da er den Ablauf der Ausführung steuert. Es gibt jedoch viele Fälle, in denen wir den ternären Operator in Situationen verwenden können, in denen IF nicht verwendet werden kann. Meiner Meinung nach ist der ternäre Operator ein Werkzeug der Mittelstufe. Sie müssen bereits über ein solides Verständnis der anderen Operatoren verfügen, bevor Sie die Vorteile des Programms voll ausschöpfen können. Darüber hinaus müssen Sie auch wissen, wie sich die Flusskontrolle innerhalb der ternären Struktur verhält. Es wird selten isoliert verwendet. Meistens wird er mit einem Zuweisungsoperator oder einem logischen Operator gepaart. Deshalb werde ich vorerst nur erklären, wie man sie liest. Ich werde versuchen, ihn noch nicht zu nutzen, zumindest noch nicht.
Schauen wir uns ein einfaches Codebeispiel an, bei dem der ternäre Operator angewendet werden könnte. Sicher, es gibt auch andere Möglichkeiten, dies zu schreiben. Aber das Ziel ist rein didaktisch. Machen Sie sich also keine Gedanken darüber, ob eine andere Methode verwendet werden kann. Konzentrieren Sie sich einfach auf das Konzept.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define PrintX(X) Print("Factoring: { ", #X, " } is: ", X) 05. //+------------------------------------------------------------------+ 06. void OnStart(void) 07. { 08. char v1 = 9, 09. v2 = 5; 10. 11. PrintX(v1); 12. PrintX(v2); 13. PrintX(v1 * ((v2 & 1) == true ? v2 - 1 : v2 + 1)); 14. PrintX(v2++); 15. PrintX(v1 * ((v2 & 1) == true ? v2 - 1 : v2 + 1)); 16. PrintX(v1); 17. PrintX(v2); 18. } 19. //+------------------------------------------------------------------+
Code 06
Wenn Code 06 ausgeführt wird, sehen Sie folgendes Ergebnis:
Abbildung 08
Da der ternäre Operator für viele Menschen, insbesondere für Anfänger, recht verwirrend sein kann. Ich werde genau erklären, was hier passiert. Dennoch sollten Sie sich die Zeit nehmen, diesen ganzen Artikel gründlich zu lesen, um ihn wirklich zu verstehen. Lesen Sie ihn langsam durch und achten Sie genau darauf, was ich zu erklären versuche. Das Thema ist sehr umfangreich, und es könnte einige Zeit dauern, bis man es vollständig verstanden hat.
Gehen wir zurück zu Code 06. Das meiste ist leicht zu verstehen. Natürlich verwenden wir ein Makro, von dem ich noch nicht erklärt habe, wie Sie es in Ihre eigenen Programme integrieren können. Aber auch dieses Makro ist relativ leicht zu verstehen. Da sie in anderen Beispielen in diesem Artikel verwendet wurde, gilt die Erklärung hier auch für diese. Das in Zeile 4 definierte Makro ermöglicht es uns, Informationen an das Terminal zu senden, die auf den im Code verwendeten Daten basieren. Aber wie genau? Zu diesem Zweck übergeben wir ihm ein Argument. Wenn diesem Argument ein Rautensymbol ( # ) vorangestellt ist, wird der Compiler angewiesen, das Argument zu übernehmen und es genau so darzustellen, wie es geschrieben wurde. Auf diese Weise können wir die durchgeführte Berechnung zusammen mit dem sich daraus ergebenden Wert und in einigen Fällen auch mit dem Namen der verwendeten Variablen anzeigen. Es ist eine großartige Möglichkeit, bestimmte Arten von Code zu debuggen.
Aber das ist nur ein Teil der Geschichte. Hier kommt es darauf an, wie die Zeilen 13 und 15 funktionieren, da in beiden Zeilen der ternäre Operator verwendet wird. Um dies auf einfache Weise zu erklären, konzentrieren wir uns auf eine dieser Linien als Referenz, da sich die andere in ähnlicher Weise verhält.
Nehmen wir also Zeile 13 und schreiben sie so um, dass sie nicht den ternären Operator, sondern die IF-Anweisung verwendet. Aber warum eine IF-Anweisung verwenden, um den ternären Operator zu erklären? Denn der ternäre Operator ist im Wesentlichen ein komprimiertes IF mit dem zusätzlichen Vorteil, dass er als Ausdruck fungiert, d. h. er kann an der Stelle platziert werden, an der normalerweise eine Variable oder ein Wert stehen würde. Trotz dieser Ähnlichkeit ersetzt der ternäre Operator jedoch nicht die IF-Anweisung. Der ternäre Operator kann in seiner Struktur keine vollständigen Codeblöcke enthalten, im Gegensatz zu IF, das zwar Codeblöcke zulässt, aber nicht als Ausdruck mit Wertrückgabe verwendet werden kann.
Na gut. Übersetzt man also Zeile 13 in eine äquivalente Form unter Verwendung einer IF-Anweisung, ergibt sich etwa folgendes Bild:
if ((v2 & 1) == true) value = v1 * (v2 - 1); else value = v1 * (v2 + 1); Print("Factoring: { v1 * ((v2 & 1) == true ? v2 - 1 : v2 + 1) } is: ", value);
Das einzige Detail, das hier zu beachten ist, ist der Variablenwert, der technisch gesehen nicht existiert. Ich verwende sie nur, um zu veranschaulichen, wie Dinge vom Compiler interpretiert werden. Betrachten Sie „value“ also als eine temporäre Variable, auf die Sie keinen direkten Zugriff haben werden.
Wenn man sich dieses Konzept vor Augen hält, wird es einfacher zu verstehen, wie der Compiler einen ternären Operator interpretiert. Beachten Sie, dass der Code bei Verwendung einer IF-Anweisung viel lesbarer wird. Natürlich wäre dies in Situationen, in denen der ternäre Operator wirklich notwendig ist, nicht praktikabel. Aber für Unterrichtszwecke funktioniert es. Das Gleiche gilt für den Befehl Print, der in diesem kleinen Übersetzungsausschnitt zu sehen ist. Dieser Befehl stellt dar, was das Makro im eigentlichen Code tun würde.
Auf den ersten Blick mag diese Sache recht komplex erscheinen. Vor allem, weil, wie ich bereits erwähnt habe, diese Art der Kodierung ein Konzept darstellt, das ich für ein mittleres Niveau halte. Also, überstürzen Sie nichts, liebe Leserin, lieber Leser. Nehmen Sie sich Zeit zum Lernen und üben Sie schrittweise. Aber wenn Sie erst einmal verstanden haben, dass der ternäre Operator im Wesentlichen ein spezialisierter IF ist, wird alles viel leichter zu verstehen sein, wenn wir in den nächsten Artikeln weitergehen.
Abschließende Überlegungen
In diesem Artikel habe ich versucht, eines der komplexesten Themen der Programmierung, zumindest aus theoretischer Sicht, vorzustellen und zu erklären. In der Praxis ist das Thema der Operatoren viel einfacher und intuitiver zu erlernen. Wenn man nämlich sieht, was die einzelnen Aktionen oder Implementierungen als Ergebnis haben, wird es viel einfacher zu verstehen, dass die Programmierer nicht versuchen, etwas Unbekanntes zu schaffen. Jedes Programm ist darauf ausgerichtet, eine Frage zu beantworten, auf die wir die Antwort bereits kennen. Wenn sich die Anwendung jedoch durch Testen und Verfeinern weiterentwickelt, verwenden wir sie schließlich, um schnellere Antworten auf spezifische, zuvor gelöste Probleme zu erhalten.
Abschließend möchte ich Ihnen diesen Ratschlag mit auf den Weg geben: Studieren Sie und üben Sie unbedingt mit verschiedenen Arten von Herausforderungen. Nur durch konsequentes Üben werden Sie in der Lage sein, den Umgang mit den einzelnen Operatoren wirklich zu beherrschen. Gehen Sie nicht davon aus, dass theoretisches Wissen ausreicht, denn das wird es nicht. Wenn es um die Zusammenarbeit mit den Operatoren geht, hat die Erfahrung ein viel größeres Gewicht als die Theorie. Also fangen Sie an zu üben.
Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/15440
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.





- 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.