I think it is best to remember that each type of loop (and programming concept in general) is like a tool in your tool box. Just as there are many different types of hammers and screwdrivers, and you can often get the job done without the one specifically required for the task, you can complete a programming task with a variety of tools, depending on approach. A hammer will drive a nail no matter it's designed/intended purpose and one can use a flat-head screwdriver to screw in a Philips-head screw. In my opinion, what is important for us to remember as programmers is to find the most-efficient solution (in terms of system resources, i.e. performance time, processing needs, I/O requirements) that will also be reasonably easy to read and maintain (by both us and others).
Now, having said that, let's look at these different types of loops and discuss where I use them. Different people might have different specific situations where they feel that one loop should be used over another, but these are my general guidelines.
Do-While
This loop is for where you want to guarantee that it performs its operations at least once, hence why the condition is checked after the first iteration. So a code block to get a number, as in the Mario lab, is a great example.
int number;
do
{
number = get_int("Enter a number: ");
}
while(number < 1 || number > 8);
This is a much more concise and straightforward way to write this operation. Here, the choice is made for ease of reading and maintainability. You could do this with a while
loop, but you would need to either initialize the variable with a value first or perform the get_int()
operation first (my preference would be to initialize it outside of the bounds of the while
loop's condition):
//initializing the variable first
int number = 0;
while(number < 1 || number > 8)
{
number = get_int("Enter a number: ");
}
//performing desired operation before loop
int number = get_int("Enter a number: ");
while(number < 1 || number > 8)
{
number = get_int("Enter a number: ");
}
To my eyes, the do-while
loop is the cleanest solution here because you only need to write the get_int()
operation once (I want to minimize the amount of repeated code as much as possible). This becomes even more true when you remember that you will not always be declaring your variables immediately before the loop. This variable could have already existed and now you are performing operations on it; replicating the do-while
loop with a while
loop becomes less straight forward in that case (but by how much will depend on the situation).
The for
loop would not make much sense here. Perhaps you could find a better solution, but we would need to ensure that a user does not enter a number that would cause the loop to exit, and there would be no guarantees without nesting more loops (at least that I can think of off the top of my head at this moment without making things even more complicated).
for(int number = 0; number > 1 || number < 8;
get_int("Enter a number: ");
Now, I have not tested that for
loop, but it should technically work. That being said, I would declare this bad code as it is not very easy to read or maintain. I say this because it would possibly (we all process things differently) require more than one pass to understand and having a complex operation as the iterator might increase the chances of an error when updating/writing this loop.
So, here the preference would be the do-while
simply because it is the cleanest and most straightforward solution, not because it is the only solution.
While
This loop is for when you know you need a loop to iterate a number of times, but you don't know how many times that should be, and it is acceptable for the loop to never execute at all. The Cash problem was a perfect example of this.
//cash contains dollar amount in cents
while(cash > 25)
{
cash = cash % 25;
coins++; //update the number of coins we have
}
We don't care if this loop never executes, because that means that there is not enough money to pull a quarter from it. But, if we used a do-while loop:
//cash contains dollar amount in cents
do
{
cash = cash % 25;
coins++;
}
while(cash > 25);
What happens now when? You will get no error from your program--it will compile and run. And, cash = cash % 25;
probably won't cause an error, either (smaller_number % bigger_number = smaller_number
). However, coins++;
will still increase the amount of coins, even though the rest of the loop won't change anything and the condition will cause the loop to iterate no more. So, this will leave you with a logic error that you will need to debug and isn't a good solution.
The for
loop could also be used:
//cash contains dollar amount in cents
for(cash; cash > 25; coins++)
{
cash = cash % 25;
}
But, you have to be weary of another logic/programming error here. Using an incrementer (coins++
here) that is unrelated to the loop condition (`cash > 25) can cause you to run into an infinite loop situation. Sure, it might not seem that likely here (and that it would be easy to debug/catch), but what about when the loop is much longer/more complicated? So, perhaps you decide that you will fix this by modifying the loop slightly:
//cash contains dollar amount in cents
for(cash; cash > 25; cash % 25)
{
coins++;
}
Logic error corrected right? Sure, but, is this really clearer to read than the while
loop?
//cash contains dollar amount in cents
while(cash > 25)
{
cash = cash % 25;
coins++;
}
There is a specific differentiation I was taught to differentiate when to use a while
loop versus a for
loop, and it is about that "you know you need a loop to iterate a number of times, but you don't know how many times that should be" that I mentioned at the start of this section.
For
I was taught, and still use this as my rule of thumb, that a for
loop should be used when you know the desired number of iterations; a while
loop is for those times when you don't. The Mario problem is a great example, but given I do not know that you are past it (and it is a great piece of logic to ensure you figure out and understand), I do not want to give away the solution. So, let's look at common lab from one of my early classes: print all even numbers from 0 to 100. Can this be done in all three loops? Yes! But, how does it look?
//Do-While loop
int i = 0;
do
{
if(i % 2 == 0)
{
printf("%i", i);
}
i++;
}
while(i < 101);
//While loop
int i = 0;
while(i < 101)
{
if(i % 2 == 0)
{
printf("%i", i);
}
i++;
}
//For loop
for(int i = 0; i < 101; i++)
{
if(i % 2 == 0)
{
printf("%i", i);
}
}
Here, you can really see the difference between the three loop types. The do-while
loop is the most verbose, and since we know we need it to iterate more than once, there is really no need to have the extra code that ensures it will iterate at least once. The while
loop works fine, but it requires that we declare and initialize the variable before the loop starts and takes up more space/line. There is nothing wrong with this, but why do more work than necessary when a clean solution like the for
loop can get all of this done without be confusing to anyone that needs to read/review/maintain this bit of code.
Now, I know this was a long post, but it's because there isn't really a right or wrong answer. There are better choices/solutions in certain situations, but you won't know what those are until you get there and have enough experience to know what is best. This is true of many things in programming, though, and why it is important to work on/review as much as you can when starting out (in my opinion).
As a quick aside, it is important to remember that this overlap in the function of loops, is also why you will find that some languages do not have all of the loop options of others. C does not have a foreach
loop, but Java and other languages do. This loop is just another tool. And, Python does not have a do-while
loop and its for
loop is more akin to Java's foreach
than C's for
, so you will always need to know how to use your available tools in your given language. This is just a cool (in my mind) bit of knowledge to know as you go further into programming that I will provide since I think this post is long enough and informative enough to give you some extra context if you've made it this far.
Hopefully this answers your questions/clears this up some for you. If it does, feel free to click the check mark next to my answer. But if not, let me know and I will try to help you further.