
From Basic to Intermediate: Definitions (I)
Introduction
The materials provided here are for educational purposes only. It should not be considered in any way as a final application. Its purpose is not to explore the concepts presented.
In the previous article "From Basic to Intermediate: Recursion" we covered what recursion is and how it can be used as a very useful programming technique in various scenarios. It allows us to create mechanisms and implementations in a simple and easy way. Although, we must take into account the possibility that the code will sometimes run slower, therefore patience should be shown in such cases.
But generally, recursion helps us and makes our lives much easier. Thus, it should be a subject of learning for those who want to become an experienced specialist (or at least have a good level of understanding the programming related things, in general).
This article considers the subject already covered before: the use of definitions, both to build macros and to declare as well as more finely control certain parts that may be implemented by us or by other programmers whose code you're interested in and which you want to amend in a controlled way to achieve certain goals.
What definitions will we have?
Definitions can take different forms within the same code and undergo minor changes related to concepts and intended use, depending on the language and the goal the programmer wants to achieve. In general, the definition helps us to control and change the code easily, quickly and safely by creating macros, compilation directives or declaring special constants. In any case, you are going to like the definitions, and you will like them very much if trying to understand how they run and will work on using them whenever possible. But most of all, you'll enjoy using definitions if you like making minor changes to your code to test the results of those changes. This is possible due to the definitions that allow this to be done in a very simple and practical way.
Further, we will look at the definitions oriented at the use of compilation directives. There are also other types of definitions that are created when using external programming, as well as when importing the code as created in other environments for joint use with MQL5. However, these definitions require us to have experience in embedded programming, even if we are going to use only MQL5 as our main language.
For example, MQL5 allows you to import code and functionality created by other programmers, typically in C or C++. This allows you to add functionality or elements of personal interest to MetaTrader 5, such as a video player, or even develop a utility for creating advanced scientific charts, with the option to use a language like LaTeX, which is a language that allows you to format mathematical expressions, should you not know. It is very interesting, by the way.
Since we will focus on compilation directives as a way to create definitions, the subject will be much simpler and more enjoyable. And I am sure you will understand it quickly, since we have already presented it in one form or another in previous articles. So, we'll start with #define directive itself.
This compilation directive is very specific. This is due to the fact that when using it, we will be able to create: a constant or a macro. That's basically all we can do using this directive. Like #include directive, which was already covered in another article, in this case From Basic to Intermediate: The Include Directive. This #define directive, along with other compilation directives, allows us to generate a number of small, easy to create and modify settings. For example, in the previous article we saw the following code:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. const uchar who = 6; 07. 08. Print("Result: ", Fibonacci_Recursive(who)); 09. Print("Result: ", Fibonacci_Interactive(who)); 10. } 11. //+------------------------------------------------------------------+ 12. uint Fibonacci_Recursive(uint arg) 13. { 14. if (arg <= 1) 15. return arg; 16. 17. return Fibonacci_Recursive(arg - 1) + Fibonacci_Recursive(arg - 2); 18. } 19. //+------------------------------------------------------------------+ 20. uint Fibonacci_Interactive(uint arg) 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
Now notice that in line 6 we have defined a constant of uchar type. But note that the functions that must receive a value expect to receive a value of uint type, as one can see in lines 12 and 20. But you shouldn't constantly adjust to this. Then we can use a compilation directive to make things like this nicer. At the same time, in some cases it can make the code a little faster. Below you can see why using a compilation directive instead of a constant can make our code a little faster, as well as other things that also help improve overall performance.
Assuming we want to use a compilation directive here, code 01 can be rewritten as follows:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_Fibonacci_Element 6 05. //+------------------------------------------------------------------+ 06. void OnStart(void) 07. { 08. Print("Result: ", Fibonacci_Recursive(def_Fibonacci_Element)); 09. Print("Result: ", Fibonacci_Interactive(def_Fibonacci_Element)); 10. } 11. //+------------------------------------------------------------------+ . . .
Code 02
Of course, we will not repeat the entire code, as there is no need for that. However, look how we implemented it. It seems not to make any difference, but there really is a difference, my dear reader. To those reading the code, it may look the same, but to the compiler, code 01 is different from code 02 precisely because in code 02 we use #define directive.
So what the compiler will actually see is the code shown below:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print("Result: ", Fibonacci_Recursive(6)); 07. Print("Result: ", Fibonacci_Interactive(6)); 08. } 09. //+------------------------------------------------------------------+ . . .
Code 03
But wait a second: it is equivalent to code 01. Therefore, there is no point in using this directive. In fact, everything is very logical. We just have to remember that we are interested in things for educational purposes here. There may be a large number of positions in our code that must use a specific value. If we use a constant, even if it is a global constant, at some point we may have problems precisely because this constant exists.
However, if we use a definition as shown in code 02, we can gain much more control over the code. Furthermore, a constant, unless it is global, will be stored, or rather visible, only within the procedure where it was declared. On the other hand, it is a directive - yes, because once it is declared, we can use it anywhere in the code without any problems as long as it exists. I say this because, unlike global constants, a directive can be destroyed at any time, its value can be changed, or it can even have a completely different function than another directive with the same name.
Now let's take this one by one as each point mentioned here is important and you may need it at some time. Now, let us look at the visibility issue first, it is a pretty simple case, as you can see in the code below:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_Fibonacci_Element_Default 6 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. 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 04
Note the following: on line 4 we declare a constant type compilation directive. It is visible throughout the code body and has the same type and expected result when we define a default value for an argument in a function or procedure. However, the same compilation directive can be replaced with a global constant. But this is where things get interesting, because we cannot do what the code below shows with a constant:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_Fibonacci_Element_Default 6 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. #undef def_Fibonacci_Element_Default 21. //+------------------------------------------------------------------+ 22. uint Fibonacci_Interactive(uint arg = def_Fibonacci_Element_Default) 23. { 24. uint v, i, c; 25. 26. for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2) 27. v += i; 28. 29. return (c == arg ? i : v); 30. } 31. //+------------------------------------------------------------------+
Code 05
Notice that on line 20 of this code we use another compilation directive. This is #undef. When using this directive we can remove or destroy a directive defined with the name contained in #undef directive. Such features are very useful. However, before we talk about more critical utilities, we need to discuss what happens when we try to compile code 05. When trying to compile, the compiler will return the following error:
Figure 01
It indicates that the error occurred on line 22. However, the def_Fibonacci_Element_Default constant was destroyed on line 20. So when the compiler tries to find the named constant in the database to compile the code, it will not find it. This will result in the error you can see in Figure 01. For this reason, many programmers have a habit of prefixing the error to help identify the type of error being generated. This is not a rule, but a good programming practice. For example, I like to prefix any defined constant with def_, so I can distinguish a general constant from a compilation directive.
"Okay, but what if we want to declare another value for the directive right after it is destroyed, can we do that?" Of course, my dear reader! But we just have to be careful about it. Later I will show you how to avoid this and avoid unnecessary headaches. It's not a 100% instrument, but at least it helps. However, let's see what happens if we change the directive as proposed.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_Fibonacci_Element_Default 6 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. #undef def_Fibonacci_Element_Default 21. //+----------------+ 22. #define def_Fibonacci_Element_Default 7 23. //+------------------------------------------------------------------+ 24. uint Fibonacci_Interactive(uint arg = def_Fibonacci_Element_Default) 25. { 26. uint v, i, c; 27. 28. for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2) 29. v += i; 30. 31. return (c == arg ? i : v); 32. } 33. //+------------------------------------------------------------------+
Code 06
So, in code 06 we see how the proposal was implemented. Note that on line 22 we define a new value for the directive, and when this code is executed, the result will be as shown below:
Figure 02
If you have noticed, the results are different, but this is to be expected precisely because of the change in the value. If the directive defined on line 4 were a global constant, we would not be able to make the change we show on line 22, nor would we be able to remove the global constant from other parts of the code. This is the basis you should try to understand. It is not just something you have to memorize, but something you have to understand and adopt.
Okay, we have seen that #define and #undef compilation directives can be used together in a simple way. Because, as I mentioned recently, there are more sophisticated ways to use these two directives together. However, to do this we need to use other directives that simplify the work and facilitate control.
Controlling the code version
One of the most interesting uses of #define and #undef directives is to control versions of the same code. Since this code for calculating an element in the Fibonacci sequence is very easy to understand, we will use it to explain version control.
Suppose we don't know how to create a version of the iterative calculation for a given element of the Fibonacci sequence, or we want to create a calculation that is different from the one shown in the code so far. You may think that this is easy to do, but sooner or later you will make an error. However, if you use compilation directives, the risk of making an error is greatly reduced. For example, we can isolate computations to choose whether we want iterative or recursive ones. To do this, simply create something similar to what is shown below:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define DEF_INTERACTIVE 05. //+----------------+ 06. #define def_Fibonacci_Element_Default 6 07. //+------------------------------------------------------------------+ 08. void OnStart(void) 09. { 10. Print("Result: ", Fibonacci()); 11. } 12. //+------------------------------------------------------------------+ 13. #ifndef DEF_INTERACTIVE 14. //+----------------+ 15. uint Fibonacci(uint arg = def_Fibonacci_Element_Default) 16. { 17. if (arg <= 1) 18. return arg; 19. 20. return Fibonacci(arg - 1) + Fibonacci(arg - 2); 21. } 22. //+----------------+ 23. #endif 24. //+------------------------------------------------------------------+ 25. #ifdef DEF_INTERACTIVE 26. //+----------------+ 27. uint Fibonacci(uint arg = def_Fibonacci_Element_Default) 28. { 29. uint v, i, c; 30. 31. for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2) 32. v += i; 33. 34. return (c == arg ? i : v); 35. } 36. //+----------------+ 37. #endif 38. //+------------------------------------------------------------------+
Code 07
Here we show the first alternative to see what we can do. Note that on line 4 of code 07 we create a definition. It doesn't necessarily have to contain any value, it just has to exist, or rather, it has to be possible for the compiler to see it. Now notice that on line 10 we only have one Fibonacci function. But I ask you: which one will be used - the one on line 15 or the one on line 27? At this point you might say: this is senseless. Can there be two functions with the same name? Yes, but we'll talk about that another time. For now, let us focuse upon what is shown in code 07.
If you have never seen a structure like this, you will have a hard time understanding what is going on here. The thing is, it doesn't make any sense at first glance. But look closely at the code. Notice that on line 13 we use another compilation directive. This checks whether the declared directive exists or not, and the code between the given #ifndef and #endif directives will only be compiled if the directive DOES NOT EXIST. Otherwise, the code will not be compiled. Something similar happens on line 25, where the code is only compiled when the directive EXISTS. That is, since line 4 defines the directive in which we want to use the code, only the code between lines 25 and 37 will be compiled. Meanwhile, the code between lines 13 and 23 is ignored.
Isn't that right? Let us test this practically. To make this really acceptable, let us add a small line to code 07, so it will look like this:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define DEF_INTERACTIVE 05. //+----------------+ 06. #define def_Fibonacci_Element_Default 6 07. //+------------------------------------------------------------------+ 08. void OnStart(void) 09. { 10. Print("Result: ", Fibonacci()); 11. } 12. //+------------------------------------------------------------------+ 13. #ifndef DEF_INTERACTIVE 14. //+----------------+ 15. uint Fibonacci(uint arg = def_Fibonacci_Element_Default) 16. { 17. Print("Testing ", __LINE__); 18. if (arg <= 1) 19. return arg; 20. 21. return Fibonacci(arg - 1) + Fibonacci(arg - 2); 22. } 23. //+----------------+ 24. #endif 25. //+------------------------------------------------------------------+ 26. #ifdef DEF_INTERACTIVE 27. //+----------------+ 28. uint Fibonacci(uint arg = def_Fibonacci_Element_Default) 29. { 30. uint v, i, c; 31. 32. Print("Testing ", __LINE__); 33. for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2) 34. v += i; 35. 36. return (c == arg ? i : v); 37. } 38. //+----------------+ 39. #endif 40. //+------------------------------------------------------------------+
Code 08
When we run code 08, we see the following:
Figure 03
Now let us change line 4 to what is shown below:
// #define DEF_INTERACTIVE
Next, compile code 08 again, and the result will be as follows:
Figure 04
This is it! Proven: it works. In one version we use recursive computations, in the other - iterative ones. And to select which version to use, it is enough to change one code line. Isn't such implementation and use of directives in our code interesting? But it is not just about that. We can do something even more interesting. Please note that the code shown can be restored in a much simpler way.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define DEF_INTERACTIVE "Interactive" 05. //+----------------+ 06. #define def_Fibonacci_Element_Default 6 07. //+------------------------------------------------------------------+ 08. void OnStart(void) 09. { 10. Print("Result to ", 11. #ifdef DEF_INTERACTIVE 12. DEF_INTERACTIVE, " : " 13. #else 14. "Recursive : " 15. #endif 16. , Fibonacci()); 17. } 18. //+------------------------------------------------------------------+ 19. uint Fibonacci(uint arg = def_Fibonacci_Element_Default) 20. { 21. #ifdef DEF_INTERACTIVE 22. 23. uint v, i, c; 24. 25. for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2) 26. v += i; 27. 28. return (c == arg ? i : v); 29. 30. #else 31. 32. if (arg <= 1) 33. return arg; 34. 35. return Fibonacci(arg - 1) + Fibonacci(arg - 2); 36. 37. #endif 38. } 39. //+------------------------------------------------------------------+
Code 09
In code 09 we use directives in a more organized way because we can control multiple things by changing just one line of code, as we did in code 08. So, what we will do is the following: first, we will run the code as shown above, that is, with the existing line 4. The result of execution can be seen just below:
Figure 05
If arranging line 4 in the same way as it was done in code 08, the result of executing the code 09 will be as follows:
Figure 06
“Isn’t it crazy? Now I am very interested in understanding this, because I did not understand what happened in code 09. So could you please explain to me what happened and why such results were obtained?”. Of course I can, my dear reader. This is exactly why this article was written.
What we did in code 09 was just a little joke to show that we can do much more with less. I know many people find it difficult to understand how programmers think, but it's actually not that difficult. Good programmers are always looking for ways to save work and increase performance. And in code 09 we do exactly that, but in a rather more creative manner.
I think you get the idea behind #ifdef directive, but let us make it even more interesting. The IF statement, which we discussed in another article, works exactly the same in #ifdef and #ifndef directives. That is, everything that is inside the chunk will either be executed or not. However, in the if statement, the chunk is delimited by opening and closing parentheses. Here #ifdef or #ifndef directive is closed by #endif directive. ALWAYS. However, it may happen that we were testing something and did not want to repeat the directive statements. In this case, inside the constructed chunk of #ifdef or #ifndef directive, we can place #else directive.
Note that just like with if statement, when the expression evaluates to true and we can execute whatever is in else which is associated with that if statement, we can do the same thing here using #ifdef and #ifndef directives. That is, understanding how IF statement works helps us better implement and use similar directives, since we can place #ifdef and #ifndef directives to test specific parts of the same code we want to implement.
Although we do not do it here, you should understand that it is possible. Now let us return to the code itself. Note that on line 4 we define something. Whereas, we can use #ifdef and #ifndef directives to model our own code. But one might wonder if during this definition on line 4 we are doing the same thing that on line 6, and whether this might interfere with #ifdef and #ifndef compilation directives. No, it is not so. This happens because #ifdef and #ifndef check whether there is a definition or not. In fact, there is #if directive, but not in MQL5, but in C and C++, where we can check the value that we assigned in the directive. However, I believe that for reasons of language simplicity, #if directive was not included, only #ifdef and #ifndef directives.
So, you can assign a value to the directive and turn it into a named constant as shown on line 12 and use it. From now on we can use the directive just like a regular constant, so it is perfectly clear for those who have read the articles and understand how code 09 works. But we can do something even more interesting that will help us understand how to manipulate data using compilation directives, and that can be useful to us in our daily lives.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define DEF_INTERACTIVE 05. //+----------------+ 06. #ifdef DEF_INTERACTIVE 07. #define def_OPERATION "Interactive" 08. #else 09. #define def_OPERATION "Recursive" 10. #endif 11. //+----------------+ 12. #define def_Fibonacci_Element_Default 11 13. //+----------------+ 14. #define def_MSG_TERMINAL "Result of " + def_OPERATION + " operation of element" 15. //+------------------------------------------------------------------+ 16. void OnStart(void) 17. { 18. Print(def_MSG_TERMINAL, " ", def_Fibonacci_Element_Default, " : ", Fibonacci()); 19. } 20. //+------------------------------------------------------------------+ 21. uint Fibonacci(uint arg = def_Fibonacci_Element_Default) 22. { 23. #ifdef DEF_INTERACTIVE 24. 25. uint v, i, c; 26. 27. for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2) 28. v += i; 29. 30. return (c == arg ? i : v); 31. 32. #else 33. 34. if (arg <= 1) 35. return arg; 36. 37. return Fibonacci(arg - 1) + Fibonacci(arg - 2); 38. 39. #endif 40. } 41. //+------------------------------------------------------------------+
Code 10
Code 10 appears to be very complex and difficult to understand for those looking at it without understanding what is going on. This seems very difficult to understand. However, nothing that is done in this Code 10 is new, since everything that is done has already been explained in this article. But it can be a bit confusing if you don't practice what has been shown in this article. Since many of these codes will be available in the application and their purpose is only to show what should be changed, you can use them in real life and study every detail shown here.
Anyway, let's see what happens if we run code 10. First, let's let the directive as specified on line 4 to actually do its job. To do this we should compile the code as shown above. As a result we get the following:
Figure 07
Now try to understand what happened before the code change as shown in the below snippet. This will make work much easier in the future. Once you understand how the output shown in image 07 was created, modify the code as shown in the snippet below:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. // #define DEF_INTERACTIVE 05. //+----------------+ 06. #ifdef DEF_INTERACTIVE 07. #define def_OPERATION "Interactive" 08. #else 09. #define def_OPERATION "Recursive" 10. #endif 11. //+----------------+ 12. #define def_Fibonacci_Element_Default 11 13. //+----------------+ 14. #define def_MSG_TERMINAL "Result of " + def_OPERATION + " operation of element" 15. //+------------------------------------------------------------------+ . . .
Snippet of code 10
Be careful, the snippet shown below should not look like code 10. Although it is barely noticeable, but in fact there are changes. This change occurs precisely on line 4, where we turn the definition into a comment. As a comment, the compiler will ignore this line. Thus, the definition does not seem to have been implemented.
Although it may seem like it means absolutely nothing to the code, this simple fact causes the compiler to generate the code that is different from the previous one. When the new code is executed, we will see in the terminal what is shown in the image below:
Figure 08
I think it is now clear how to use the definition and #ifdef, #ifndef, #else and #endif directives. However, one more use of #define directive remains to be discussed. Remember, at the beginning of the article we mentioned that this directive would serve two purposes. The first one is the one we discussed in this article, and which you can practice using the codes in the application.
This allows you to use #define directive, to avoid unnecessary creation of a global variable, as well it facilitates simple, fast and efficient analysis and implementation of different versions of the same code. And this is valuable for beginners. For more experienced programmers this is a bit trivial, since they use things like this almost automatically, as it makes life much easier. I wish we had #if directives from C and C++. But there is nothing to worry about, everything is as it should be.
The second way to use #define directive is to create a macro. However, since macros take time to be analyzed without rushing, I decided not to include them for now, as tackling them right now might make them too complex to understand. This is because macros are not like simple code snippets, as many people might imagine. When used correctly, macros are a very useful tool, but when misunderstood and misused, they can make the code very confusing and complex.
Before I finish this article, I want to talk about one last thing. This will be more of a BONUS for you, because you had enough patience to read to the end, and for sure you already want to start experimenting with using #define directive.
This bonus is the ability to create several simple commands inside MQL5 without any changes, so that they make a little more sense. We'll talk more about this when we finish discussing macros, but this serves as a preview of the next topic aleady now.
You may not have noticed this, but when we use #define directive, we tell the compiler that a certain text should be replaced with another text. With this concept we can create alternative syntax without changing anything in the way we create codes.
To demonstrate this, let us consider the code below:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define BEGIN_PROC { 05. #define END_PROC } 06. #define RETURN return 07. #define ENTER_POINT void OnStart (void) 08. #define Z_SET_NUMBERS long 09. #define EQUAL == 10. #define IS = 11. #define MINOR < 12. #define OR || 13. #define MORE += 14. //+------------------------------------------------------------------+ 15. #define DEF_INTERACTIVE 16. //+----------------+ 17. #ifdef DEF_INTERACTIVE 18. #define def_OPERATION "Interactive" 19. #else 20. #define def_OPERATION "Recursive" 21. #endif 22. //+----------------+ 23. #define def_Fibonacci_Element_Default 11 24. //+----------------+ 25. #define def_MSG_TERMINAL "Result of " + def_OPERATION + " operation of element" 26. //+------------------------------------------------------------------+ 27. ENTER_POINT BEGIN_PROC 28. Print(def_MSG_TERMINAL, " ", def_Fibonacci_Element_Default, " : ", Fibonacci()); 29. END_PROC 30. //+------------------------------------------------------------------+ 31. Z_SET_NUMBERS Fibonacci(Z_SET_NUMBERS arg IS def_Fibonacci_Element_Default) 32. BEGIN_PROC 33. #ifdef DEF_INTERACTIVE 34. 35. Z_SET_NUMBERS v, i, c; 36. 37. for (c IS 0, i IS 0, v IS 1; c MINOR arg; i MORE v, c MORE 2) 38. v MORE i; 39. 40. RETURN (c EQUAL arg ? i : v); 41. 42. #else 43. 44. if ((arg EQUAL 1) OR (arg MINOR 1)) 45. RETURN arg; 46. 47. RETURN Fibonacci(arg - 1) + Fibonacci(arg - 2); 48. 49. #endif 50. END_PROC 51. //+------------------------------------------------------------------+
Code 11
This code works the same as code 10, but here we create something very different from the standards that many people actually use. However, the compiler will understand code 11 just fine, so well that it will produce the same results as code 10.
But looking at it, you might think: "Man, THIS IS NOT MQL5. However, thanks to what we've seen in this article, we can take a closer look at it and see that yes, although it is different, the code 11 is pure and simple MQL5, just written differently, mostly oriented at resembling a less mathematical and more natural language.
Notice that after the definitions are added (which happened between lines 4 and 13), the rest of the code, but mostly starting from line 27 onwards, looks completely different. Many may even say that this will not result in creation of an executable file. But to everyone's surprise, yes, this code works.
Final considerations
In this article, we have implemented several things that many of you may find strange and out of context, but which, if applied correctly, will make your learning phase much more fun and exciting, as you can build quite interesting things and better understand the syntax of MQL5 language itself. Since the material presented here requires due study and practice, we will conclude the article on this point. In the next article, we will discuss the second way to use #define directive. Therefore, practice and study today's material so as not to get confused with the material in the next article.
In the application you will find the five codes shown here. So, have a good time.
Translated from Portuguese by MetaQuotes Ltd.
Original article: https://www.mql5.com/pt/articles/15573
Warning: All rights to these materials are reserved by MetaQuotes Ltd. Copying or reprinting of these materials in whole or in part is prohibited.
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.





- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use