In all the loops you have solved so far, you knew the number of repetitions. Sometimes you did not know it when writing a program because the user was supposed to enter it. However, in all the cases, when a loop started, it had already been determined how many times it would repeat.
Sometimes the number of repetitions is not known at the moment a loop starts executing. Frequently you will be concerned with the question of whether a loop should go on or terminate.
Entering a Password
The first task concerns logging in. You do not know in advance how many attempts the user will need.
Task
You will repeatedly ask the user to enter a password until the user enters the correct one (see Figure 23-1). The correct password will be friend.
Figure 23-1 Repeatedly asking a question
Solution
Here’s the code:
static void Main(string[] args)
{
string correctPassword = "friend";
bool ok; // the variable must be declared outside of the loop!
do
{
// Input
Console.Write("Enter password: ");
string enteredPassword = Console.ReadLine();
// Evaluating
ok = enteredPassword == correctPassword;
} while (!ok); // loop repeats when the condition holds
Console.WriteLine("Come inside, please...");
// Waiting for Enter
Console.ReadLine();
}
do-while Construction
To write the loop, you use the do- while construction.
The computer enters the loop after the word do, executes its statements, and asks “Once more?” If the condition after the while word holds, the computer returns to the beginning of the loop, in other words, after the do word. And so on.
The loop terminates at the moment when its condition after the while word is evaluated as unfulfilled (false).
This Case
In this case, the program evaluates the entered password after each input. The evaluation result is then stored in a bool-typed variable called ok.
You want the loop to go on if the entered password is not correct. That is why you use a negation operator (an exclamation mark) in the while condition.
Variable Outside of the Loop
C# requires that all variables used in the loop condition be declared outside of the loop. When you declare them inside, they are not visible when formulating the condition.
Tip
Visual Studio can help you with your do-while loops. Just enter do and press the Tab key twice.
Waiting for Descend
Imagine the computer watches some quantity that grows most of the time, and the task is to detect the (possibly rare) moment when it lessens (descends).
You would usually encounter such a problem when digging through a large amount of data stored in a file or in a database. However, you will solve this on some data entered by the user.
Task
You will make a program that repeatedly asks the user for input (see Figure 23-2). Whenever the user enters a number less than the previous one, the program will notify the user (and terminate).
Figure 23-2 Terminating when the number gets smaller
Solution
The core of the solution is to remember the previous value, not just the value currently entered.
Here’s the code:
static void Main(string[] args)
{
// Preparations
int previous = int.MinValue;
bool ok;
// Repeating until descend
do
{
// Input
Console.Write("Enter a value (number): ");
string input = Console.ReadLine();
int value = Convert.ToInt32(input);
// Evaluating
ok = value >= previous; // ok = still not descending
// Storing for the next round of the loop
previous = value;
} while (ok);
// Message to the user
Console.WriteLine("Descend detected...");
// Waiting for Enter
Console.ReadLine();
}
Discussion
The first value is somewhat special because it has no predecessor. Its absence can be circumvented by simulating it using some very small number. C# offers you int.MinValue, which is the least value that can be stored in the int type, which is minus two billion approximately.
Every Week Until the End of Year
Let’s proceed to the next exercise, which has to do with dates.
Task
The task is to display dates until the year’s end starting with today and proceeding in one-week steps (see Figure 23-3).
Figure 23-3 Stepping through the year, one week at a time
Solution
Here’s the code:
static void Main(string[] args)
{
// Today
DateTime today = DateTime.Today;
int thisYear = today.Year;
// Repeating
DateTime date = today;
do
{
// Output
Console.WriteLine(date.ToLongDateString());
// Preparing next output (a week later)
date = date.AddDays(7);
} while (date.Year == thisYear);
// Waiting for Enter
Console.ReadLine();
}
As Long As a 6 Is Being Thrown
Random numbers can provide you with other nice examples of the uncertain termination of a loop.
Task
You will throw a die and keep throwing it as long as there is a 6 (see Figure 23-4 and Figure 23-5).
Figure 23-4 Rolling a die once (no 6, no repetitions)
Figure 23-5 Rolling a die as long as you have a 6
You may know some board game where this principle is used.
Solution
Here’s the code:
static void Main(string[] args)
{
// Random number generator
Random randomNumbers = new Random();
// Throwing as long as we have six
int thrown;
do
{
thrown = randomNumbers.Next(1, 6 + 1);
Console.WriteLine(thrown);
} while (thrown == 6);
// Waiting for Enter
Console.ReadLine();
}
Until Second 6
This task is about the unknown number of repetitions with random values.
Task
You will write a program that throws a die until the 6 appears for the second time (see Figure 23-6).
Figure 23-6 Waiting until a 6 appears twice
Solution
You simply count the 6s.
Here’s the code:
static void Main(string[] args)
{
// Random number generator
Random randomNumbers = new Random();
// Throwing until the second six is thrown
int howManySixes = 0;
do
{
// Actual throwing
int thrown = randomNumbers.Next(1, 6 + 1);
Console.WriteLine(thrown);
// Counting sixes
if (thrown == 6)
{
howManySixes++;
}
} while (howManySixes < 2);
// Waiting for Enter
Console.ReadLine();
}
Until Two 6s in a Row
Do you know why there are so many examples of throwing dice ? I liked to play board games when I was a kid, can you tell?
Task
In this program, you will be throwing a die until a 6 is thrown twice in a row (see Figure 23-7).
Figure 23-7 Two 6s in a row
Solution
Besides the currently thrown number, you have to track the previous one as well. This is similar to the program in the “Waiting for Descend” section.
If both the current and previous numbers are 6s, the program terminates.
Again, the first value is specific in not having a predecessor. That is why the previous variable starts with 0, which is a value that can never appear on a die.
Here’s the code:
static void Main(string[] args)
{
// Random number generator
Random randomNumbers = new Random();
// Preparations
int previous = 0;
bool ending;
// Throwing until two sixes in a row
do
{
// Actually throwing
int thrown = randomNumbers.Next(1, 6 + 1);
Console.WriteLine(thrown);
// Evaluating
ending = thrown == 6 && previous == 6;
// Preparing for next round of the loop
previous = thrown;
} while (!ending);
// Waiting for Enter
Console.ReadLine();
}
Summary
In this chapter, you studied loops with the number of repetitions not known at the time the loops start. In C#, this kind of loop can be suitably written using the do-while construct. Its function is first to execute the statements of its body and then to ask “Once more?” You evaluate the condition, and if it holds, you execute another round of the loop.
You also saw that to use some variable in a do-while loop condition, the variable must be declared outside of the loop.
A frequent mistake when using the do-while loop is the wrong formulation of its condition. You have to be careful and write it in such a way that if you want to continue looping, the condition should evaluate to true.
In a couple of tasks of this chapter, you needed some value from the previous round of a loop. For this purpose, you used a special variable where you stored the value. Of course, the first round of the loop required special treatment.