
From Basic to Intermediate: WHILE and DO WHILE Statements
Introduction
The materials presented here are intended for didactic purposes only. Under no circumstances should the application be viewed for any purpose other than to learn and master the concepts presented.
In the previous article, From Basic to Intermediate: IF ELSE Statement, I explained how to examine the code to implement conditional flow control. This was done so that, depending on the expression being analyzed, we could choose whether one or another procedure would be executed.
Although the IF statement, along with its ally ELSE, is so useful that it allows us to implement almost any code, in practice this would not be very wise. Using only two statements would make some parts of the code quite difficult to understand and digest.
For this very reason, programming languages do not rely solely on the IF-ELSE pair. To make code more readable and easier to comprehend, they incorporate a broader set of instructions, including loop statements.
Loop statements, generally speaking, and without exaggeration, are among the most dangerous control flow instructions that exist. This is because even a minor programming error in implementing a loop can result in a disaster. And that is a serious issue, as your code will enter an infinite loop. Many beginners give up after encountering such a problem. They are simply horrified by the prospect of implementing loops in their code.
There are essentially two ways to create a loop in a program. The first involves using functions and procedures outside the main routine where the loop is implemented. The second is through control flow statements.
Since using control flow statements is the simpler approach, we will start with that for now. However, as we progress, I will demonstrate how to create loops using functions and procedures. Contrary to what many might assume, there is a valid reason for using a function or a procedure to create a loop instead of relying solely on control flow statements. But explaining that will be the subject of another article at a more suitable time.
There is a prerequisite for following along with this article: an understanding of variables and constants. We have already covered this topic. However, beyond just knowing what variables and constants are, the most crucial aspect here is understanding how bit-width affects values and how a variable's type can impact its value.
If you are unfamiliar with what I'm referring to, check out From Basic to Intermediate: Operators, where I explain some key concepts related to this topic. While the content there is fundamental, it should be enough for you to follow along with what will be discussed here. I strongly encourage you to explore other articles for a more comprehensive understanding of everything presented in this one.
At this point, I face a small dilemma. Although the FOR statement is generally simpler for creating loops and is often a favorite among programmers, it has certain drawbacks. On the other hand, the alternative control statements for creating loops tend to be a little riskier than the FOR statement. So, I am considering which one to introduce first. In the meantime, let's proceed as follows: we will start a new section where I will explain some important concepts related to this topic.
Why Use Loops?
Many programmers have an outright fear of creating loops in their code. Others avoid them as much as possible and only use them when there is no other way. But why is there so much fear of using loops? The reason is simple: loops inherently involve risk. Every time you enter one, you become dependent on your code's execution, requiring a specific condition to properly terminate the loop when needed. Otherwise, a loop may enter an infinite cycle without you realizing it, leading you to mistakenly believe that the computer is simply taking too long to process something, when, in fact, the code has become trapped in an endless loop.
Another reason why many developers fear loops is that analyzing what happens within them can be quite difficult. This is because, in real-world applications, loops often need to execute thousands or even millions of times before completing. Depending on the type of computation being performed, this can take hours before delivering a final result.
I know this seems unthinkable to many. How is it possible that a program takes hours to complete a task? However, it does happen. There are scenarios where completing a task genuinely requires hours of processing. That said, in most cases, especially in an educational context, the loops you will be writing will be relatively small, with a scale of thousands of iterations. Typically, this results in execution times of only a few seconds or minutes. A good example would be applications designed for training a neural network.
Through my other profile, I demonstrate how to implement such an application: a neural network written entirely in pure MQL5. In my view, this is an intermediate-level project since it requires only a solid understanding of MQL5 and the underlying mathematics. The rest is relatively straightforward.
Now, I still haven't answered the question posed in this section's title: Why use loops? The answer lies in the simplicity they bring to your code. Loops allow you to control how many times a specific operation is performed without manually repeating code or relying on Ctrl+C and Ctrl+V. Consider the following example. Earlier, I introduced a simple piece of code designed to calculate the factorial of a given number. As you may recall, the code itself was quite straightforward. For those who haven't seen it, here it is once again:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. uchar counter = 0; 07. ulong value = 1; 08. 09. Print("Factorial of ", counter, " => ", value); 10. counter = counter + 1; 11. value = value * counter; 12. Print("Factorial of ", counter, " => ", value); 13. counter = counter + 1; 14. value = value * counter; 15. Print("Factorial of ", counter, " => ", value); 16. counter = counter + 1; 17. value = value * counter; 18. Print("Factorial of ", counter, " => ", value); 19. counter = counter + 1; 20. value = value * counter; 21. Print("Factorial of ", counter, " => ", value); 22. } 23. //+------------------------------------------------------------------+
Code 01
Code 01 may be acceptable for small numbers. However, as they increase, things start to get more complicated. Even for the factorial of 20, the setup will be very difficult, in addition to the huge probability of errors in the code implementation, even using CTRL+C and CTRL+V. So, in the same article where this code 01 appears, we developed another one, which can be seen below.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Factorial(); 07. Factorial(); 08. Factorial(); 09. Factorial(); 10. Factorial(); 11. } 12. //+------------------------------------------------------------------+ 13. void Factorial(void) 14. { 15. static uchar counter = 0; 16. static ulong value = 1; 17. 18. Print("Factorial of ", counter, " => ", value); 19. counter = counter + 1; 20. value = value * counter; 21. } 22. //+------------------------------------------------------------------+
Code 02
At this point, you may notice that our code has become a bit more compact, making it slightly easier to use Ctrl+C and Ctrl+V for small-scale factorizations. However, what if you needed to factorize a significantly larger number? How can you do that? Well, assuming you didn't exceed the 64-bit upper limit of the ulong value, you would have to copy and paste the factorial layers multiple times. This, of course, does not include all the additional effort required to adjust the code each time a new factorial needed to be calculated. Clearly, this would be far more tedious than enjoyable. For this reason, using a loop makes the code much more manageable. And this explains why loops are essential.
Now that we have established this understanding, we can begin discussing the statements to be used. While the FOR loop is a favorite among many programmers (including the one writing this) let's start with a different statement. This alternative has two possible implementations, yet it is simpler to explain and, as a result, easier to grasp compared to the FOR loop, which has certain nuances that led me to begin with another approach.
WHILE and DO WHILE statements
Despite the playful wording in this section's title, this loop is quite straightforward to understand since it follows the same principles as the IF statement. However, there are some important precautions to keep in mind. It's not uncommon for developers to struggle when using this loop. The reason is simple: forgetfulness. Many programmers forget to adjust or correct the condition that determines when the loop should terminate. Surprisingly, even experienced developers make this mistake. So, consider this a warning: Be careful when implementing code that uses this statement.
The difference between WHILE and the DO-WHILE pair is quite simple. WHILE may not execute the internal routine at all, whereas DO-WHILE guarantees that the routine runs at least once. That's basically it. Simple, isn't it? However, this does not reduce the risks involved. So, let's start with the simpler of the two: the WHILE statement. Its execution flow is illustrated below.
Figure 01
Notice how it closely resembles the IF statement discussed in the previous article. In fact, the way expressions function here follows the same principles. This means the loop will execute the defined routine only if the expression evaluates to true. In other words, the expression must be nonzero for the loop to run. This is why I introduced the IF statement first. Understanding that statement is crucial for anyone aspiring to become a proficient programmer or even for those who want to write code just for fun.
Perfect. Since this structure is quite similar to the IF statement - and I hope, dear reader, that you have done your homework by studying it - we can now be more direct in our approach.
Let's start with a very simple piece of code to give you a basic introduction to loops. I don't want to make you stumble, but rather I want to help you understand how they work in practice. To do this, let's analyze the following code:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. char info = 10; 07. 08. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 09. 10. while (info) 11. { 12. info = info - 1; 13. Print(__FUNCTION__, " : ", info); 14. } 15. 16. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 17. } 18. //+------------------------------------------------------------------+
Code 03
This Code 03 effectively illustrates how a WHILE loop operates. When executed, it will produce the output shown below.
Figure 02
The highlighted section in Figure 02 represents the portion of the output generated by the loop. This is the routine executed within the WHILE loop. Now, let's take a quick look at how this occurred and why the countdown started at nine and ended at zero.
To understand this, you need to observe two key details. First, in line six, we initialize the variable with the value ten. When the WHILE statement evaluates the condition, which is the value of the variable set in line six, it determines that the condition is true, thus initiating the loop. However, and this is the second important point, before printing the value to the terminal in line 13, we decrement the variable info by one in line 12. This is why the countdown progresses from nine to zero. The loop terminates at zero because zero is considered a boolean false value.
But what would happen if, instead of decrementing the info variable in line 12, we incremented it by one? Would the loop enter an infinite cycle? That is an excellent question, my dear reader, and one you can experiment with yourself to see the results. However, before testing it, let's first understand why the loop would not enter an infinite cycle in this particular case.
Contrary to common intuition, where numbers extend from negative infinity to positive infinity, things work differently in computing. Every value a computer can express has an upper and lower limit. This limit is determined by the bit width of the variable type being used. We've discussed this in a previous article. Because of this limitation (and keep in mind that some languages do not impose such a constraint, though that is beyond our scope here) if the value change ensures that the variable eventually reaches zero, the loop will terminate at some point. Regardless of how long it takes, it will eventually end. However, there are some specific challenges with the WHILE loop in this scenario. To avoid confusion at this stage, we'll leave this discussion for another time. Most likely, in the next article.
So, that's basically all that can be said about the WHILE statement. However, there is one additional detail that may be useful to explain now. When we want a code to operate in a controlled manner, especially when it comes to loops, we often include a secondary exit condition in addition to the usual termination condition. This serves as a failsafe, allowing us to force the loop to end so that the program can continue its normal execution.
Since MQL5 is an event-driven language (a topic we will explore in more depth later), it is uncommon to create loops that wait indefinitely for an event. This behavior is more typical in languages like C or C++, which are not inherently event-driven.
In practice, we often use loops to create a controlled pause in an application. This pause remains active for a predetermined period or until a specific event occurs. However, it is not very common to use a WHILE loop for this purpose directly. The reason is that a WHILE loop will only execute if the initial condition is true. And in many cases, this is not quite what we need.
That said, in cases where an emergency exit is needed, a WHILE loop is often the better choice. If the condition evaluates to false from the start, the loop will not execute at all. This is crucial because, in many cases, the loop's routine may directly influence whether or not it should continue running, based on certain criteria. We will explore this concept further when discussing the DO-WHILE loop.
Now, let's do the following. We will intentionally create a loop that, in theory, can be infinite. However, we will also implement a condition that allows it to terminate. This approach can be seen below.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. char info = 5; 07. 08. Print("Press ESC to finish counting..."); 09. 10. while (!TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) 11. { 12. info = info - 1; 13. Sleep(200); 14. Print(__FUNCTION__, " : ", info); 15. } 16. 17. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 18. } 19. //+------------------------------------------------------------------+
Code 04
In Code 04, we have a loop in line 10 that, in theory, would execute indefinitely. However, this will not happen if the user presses the ESC key. One important detail to note is that the alert message is only displayed once, before the loop begins. If the user misses this message, which is printed to the terminal, it could lead to some confusion. In the terminal, you will see an output similar to the one shown below.
Figure 03
Notice that, this time, the value of the 'info' variable became zero, yet the loop continued. This happens because the condition that determines when the loop terminates is now dependent on whether the ESC key is pressed. In other words, the program waits for an event before allowing the execution to continue. Another common test used in MQL5 scripts is to check whether the script has been interrupted. To implement this, simply replace line 10 in Code 04 with the following line.
while ((!TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) && (!IsStopped()))
This small modification allows the loop in Code 04 to terminate in two ways: by pressing the ESC key and by removing the script from the chart manually. Simple, isn't it, my dear reader? There's no reason to fear loops. However, we still have one problem: the alert message in line 8 could easily be missed during execution. This means the user might not realize that pressing ESC will also stop the application.
One way to solve this issue is by placing line 8 inside the loop. However, instead of modifying our WHILE loop directly, let's take this opportunity to explore the DO-WHILE loop variation. For greater clarity of presentation, we will consider this in a new topic.
The DO...WHILE Loop
I couldn't resist a bit of humor in this section title. But in a way, it perfectly describes the DO-WHILE loop. Although it is a single statement, it functions more like a pair, just like IF ELSE. However, here things happen a little differently.
From our previous discussion, you may recall that a WHILE loop only executes if its condition is true. The key difference here is that the DO-WHILE loop executes its routine at least once, regardless of whether the condition is initially true or false. This is because the statement DO (which literally means "DO") comes before the WHILE condition (which means "WHILE"). This ensures that the routine inside the loop runs at least once before the condition is checked. This behavior can be particularly useful in certain scenarios, allowing us to better control how specific routines function, especially when we need them to execute at least once before evaluating a condition.
To illustrate this, let's modify Code 04 in a way that ensures the alert message appears periodically. While we could accomplish this directly in Code 04, let's assume that duplicating line 8 wasn't an option. More importantly, we want to ensure the loop executes at least once, regardless of any prior conditions.
Thus, we get the following code:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. char info = 10; 07. 08. do 09. { 10. if (((info / 5) * 5) == info) Print("Press ESC to finish counting..."); 11. info = info - 1; 12. Sleep(200); 13. Print(__FUNCTION__, " : ", info); 14. }while (!TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)); 15. 16. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 17. } 18. //+------------------------------------------------------------------+
Code 05
When you run this code, you will see something similar to what is shown below.
Figure 04
Here we are dealing with some things that are, in a certain sense, quite interesting to understand. This is because some actions in code 05 may seem illogical at first glance. But if you have been paying close attention and studying the previously published articles conscientiously, you will be able to understand them relatively quickly.
Well, let's figure out what's going on here and why the message asking you to press ESC now appears from time to time. As you can see, now the check whether to continue executing the procedure inside the loop is done in line 14, and the complete loop procedure is executed at least once. Hopefully, it's now clear why the DO element coexists in this combination with the WHILE element.
Now pay close attention to what I am about to explain, because it is very important. The idea here is the same as with the WHILE statement. The loop will execute until the WHILE statement expression becomes false. But how do you know where the loop begins? If you delete the eighth line, you won't know exactly where the loop starts. And what's worse, the procedure that needs to be in the loop, which goes from line 10 to line 13, WILL NOT BE perceived as a procedure, but rather as ordinary code.
The WHILE statement shown in line 14 will indeed be a loop. However, the procedure will be empty because no other commands will be executed inside the loop. It would just stay there, waiting for the ESC key to be pressed to terminate it.
Only based on the combination of the DO and WHILE elements, both the compiler and other programmers can determine that the loop begins on line eight and ends on line fourteen. This may all seem a bit confusing, but it will make a lot more sense if we remove the DO element from line 8, which will make the code look something like this:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. char info = 10; 07. 08. 09. { 10. if (((info / 5) * 5) == info) Print("Press ESC to finish counting..."); 11. info = info - 1; 12. Sleep(200); 13. Print(__FUNCTION__, " : ", info); 14. }while (!TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)); 15. 16. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); 17. } 18. //+------------------------------------------------------------------+
Code 06
As we can see, due to this small change in the code, which could easily go unnoticed, the result is completely different. To make things clearer, I won't use an image, but instead will show what's happening using the gif below.
Animation 01
This is where one of the dangers of creating loops without proper attention lies. Notice that we no longer see the same behavior as in fig. 04. Here, what should have been the body of the loop was executed as if it were a regular sequence of code. However, it stopped at that point, and the code entered the loop shown on line 14. As a result, if the loop's termination condition depended on an action within the loop's routine, that action would never occur. Consequently, a loop that was designed to have an exit condition would never terminate, leading to the dreaded infinite loop.
Now, let's analyze how and why the message on line 10 is being printed at intervals. To understand this, we need to examine the expression within the IF statement on line 10. While this expression might not make sense to a human, it is entirely logical to a computer, especially in a strongly typed language like MQL5.
Another article already covered mathematical calculations in programming languages. That said, this seemingly nonsensical operation of dividing the value of the variable info by a number and then immediately multiplying the result by that same number may appear pointless. However, I strongly encourage you to read the previous articles if you are just beginning to explore programming. The reason is that performing this calculation can yield a result that either matches or differs from the original value in 'info'. If the values are the same, the message will be printed. If they are different, it will not.
Since this code will be available as an attachment, you can study it more thoroughly. The key point to remember is that you must use the same value for both division and multiplication and always divide before multiplying. But be cautious to use integer values. Floating-point numbers will not work due to certain factors that we will discuss later.
To conclude this topic, let’s examine the execution flow diagram for the DO-WHILE loop. It is shown below.
Figure 05
As you can see, it is not as complex as it might have initially seemed before seeing it in action. Understanding execution flow is far more important than simply memorizing commands and syntax. Once you grasp how execution flow works, you will be able to think more clearly. With continued practice, recognize how each small decision in the flow influences the final outcome. Over time, you will naturally develop programming skills, even as a hobbyist. You will become proficient enough to learn other programming languages without being limited to just one.
Final Thoughts
In this article, I have aimed to present this topic in the most accessible and straightforward way possible. Many beginner programmers view loops as a nightmare. However, I hope to have demonstrated that with patience, focus, and careful attention, whether you are implementing the code yourself or studying another programmer's work, you can accomplish some truly interesting things. These are the things that can be extremely useful.
When used correctly and efficiently, loops can save you a significant amount of time, not only in structuring conditions but also in simplifying logic. In fact, the early examples in this article, where I demonstrated number factorization without loops, using only sequential calls, can be rewritten in a much more compact and therefore more efficient manner. This is illustrated below, showcasing how loops enable elegant and practical solutions.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. uchar counter = 0; 07. ulong value = 1; 08. 09. while (counter < 5) 10. { 11. counter = counter + 1; 12. value = value * counter; 13. } 14. 15. Print("Factorial of ", counter, " => ", value); 16. } 17. //+------------------------------------------------------------------+
Code 07
Try changing code 02 to use a loop instead of those sequential calls between lines 6 and 10. If you can't find a way to do this, don't worry. We'll talk more about loops in the next article, since we haven't covered everything yet. So start practicing before it's too late.
Translated from Portuguese by MetaQuotes Ltd.
Original article: https://www.mql5.com/pt/articles/15375





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