Repeating Program Steps - Program Statements - C# 24-Hour Trainer (2015)

C# 24-Hour Trainer (2015)

Section III

Program Statements

Lesson 19

Repeating Program Steps

One of the computer's greatest strengths is its ability to perform the exact same calculation again and again without getting bored or making careless mistakes. It can calculate the average test scores for a dozen students, print a hundred advertisements, or compute the monthly bills for a million customers with no trouble or complaining.

The lessons you've read so far, however, don't tell you how to do these things. So far every step the computer takes requires a separate line of code. To calculate bills for a million customers, you would need to write at least a million lines of code!

In this lesson you learn how to make the computer execute the same lines of code many times. You learn how to loop through arrays and collections of items to take action or perform calculations on them.

The following sections describe the kinds of loops provided by C#. The final section describes two statements you can use to change the way a loop works: break and continue.

for Loops

A for loop uses a variable to control the number of times it executes a series of statements. The for loop's syntax is as follows:

for (initialization; doneTest; next)

{

statements…

}

Where:

· initialization gets the loop ready to start. Usually this part declares and initializes the looping variable.

· doneTest is a boolean expression that determines when the loop stops. The loop ­continues running as long as this expression is true.

· next prepares the loop for its next iteration. Usually this increments the looping variable declared in the initialization.

· statements are the statements that you want the loop to execute.

Note that none of the initialization, doneTest, or next statements are required, although they are all used by the simplest kinds of for loops.

For example, the following code displays the numbers 0 through 9 followed by their squares in the Console window:

for (int i = 0; i < 10; i++)

{

int iSquared = i * i;

Console.WriteLine(string.Format("{0}: {1}", i, iSquared));

}

In this code the initialization statement declares the variable i and sets it to 0, the next statement adds 1 to i, and the doneTest keeps the loop running as long as i < 10.

Here's a slightly more complicated example that calculates factorials. The program converts the value selected in the NumericUpDown control named numberNumericUpDown into a long integer and saves it in variable n. It initializes the variable factorial to 1 and then uses a loop to multiply factorial by each of the numbers between 2 and n. The result is 1 * 2 * 3 * … * n, which is n!:

// Get the input value N.

long n = (long)numberNumericUpDown.Value;

// Calculate N!.

long factorial = 1;

for (int i = 2; i <= n; i++)

{

checked

{

factorial *= i;

}

}

// Display the result.

resultTextBox.Text = factorial.ToString();

You may recall that Lesson 16 used code to calculate Fibonacci numbers, and in that lesson's Exercise 1 you calculated factorials. Those programs used 20 lines of code to calculate and store 20 values that the program then used as a kind of lookup table.

The factorial code shown here uses a lot less code. It doesn't require a large array to hold values. It also doesn't require that you know ahead of time how many values you might need to calculate (20 for the earlier programs), although the factorial function grows so quickly that this program can only calculate values up to 20! before the result won't fit in a long.

NOTE

The for loop is often the best choice if you know exactly how many times you need the loop to execute.

Foreach Loops

A foreach loop executes a block of code once for each item in an array or list. The syntax of the foreach loop is as follows:

foreach (variableDeclaration in items)

{

statements…

}

Where:

· variableDeclaration declares the looping variable. Its type must be the same as the items in the array or list.

· items is the array or list of items over which you want to loop.

· statements are the statements that you want the loop to execute.

For example, the following code calculates the average of the test scores stored in the ListBox named scoresListBox. Note that the ListBox must contain integers or something the program can implicitly convert into an integer or else the program will crash:

// Add up the values.

int total = 0;

foreach (int value in valuesListBox.Items)

{

total += value;

}

// Calculate the average.

float average = (float)total / valuesListBox.Items.Count;

The code creates a variable named total and sets it equal to 0. It then loops through the items in the ListBox, adding each value to total.

WARNING

This code loops over the items in a ListBox, treating those items as integers. If the ListBox contains something other than integers, the program will crash.

The code finishes by dividing the total by the number of items in the ListBox.

NOTE

If you need to perform some operation on all of the items in an array or list, a foreach loop is often your best choice.

while Loops

A while loop executes as long as some condition is true. The syntax for a while loop is as follows:

while (condition)

{

statements…

}

Where:

· condition is a boolean expression. The loop executes as long as this expression is true.

· statements are the statements that you want the loop to execute.

For example, the following code calculates a number's prime factors:

// Find the number's prime factors.

private void factorButton_Click(object sender, EventArgs e)

{

// Get the input number.

long number = long.Parse(numberTextBox.Text);

// Find the factors.

string result = "1";

// Consider factors between 2 and the number.

for (long factor = 2; factor <= number; factor++)

{

// Pull out as many copies of this factor as possible.

while (number % factor == 0)

{

result += " x " + factor.ToString();

number = number / factor;

}

}

// Display the result.

resultTextBox.Text = result;

}

The code starts by getting the user's input number. It builds a result string and initializes it to “1.”

Next the code uses a for loop to consider the numbers between 2 and the user's number as possible factors.

For each of the possible factors, it uses a while loop to remove that factor from the number. As long as the factor divides evenly into the remaining number, the program adds the factor to the result and divides the user's number by the factor.

The code finishes by displaying its result.

NOTE

Loops that use incrementing integers to decide when to stop are often easier to write using for loops instead of while loops. A while loop is particularly useful when the stopping condition occurs at a less predictable time, as in the factoring example.

do Loops

A do loop is similar to a while loop except it checks its stopping condition at the end of the loop instead of at the beginning. The syntax of a do loop is as follows:

do

{

statements…

} while (condition);

Where:

· statements are the statements that you want the loop to execute.

· condition is a boolean expression. The loop continues to execute as long as this expression is true.

The following code uses a do loop to calculate the greatest common divisor (GCD) of two numbers, the largest number that divides them both evenly:

// Calculate GCD(A, B).

private void calculateButton_Click(object sender, EventArgs e)

{

// Get the input values.

long a = long.Parse(aTextBox.Text);

long b = long.Parse(bTextBox.Text);

// Calculate the GCD.

long remainder;

do

{

remainder = a % b;

if (remainder != 0)

{

a = b;

b = remainder;

}

} while (remainder > 0);

resultTextBox.Text = b.ToString();

}

NOTE

Notice that the variable remainder used to end the loop is declared outside of the loop even though it doesn't really do anything outside of the loop. Normally to restrict scope as much as possible, you would want to declare this variable inside the loop if you could.

However, the end test executes in a scope that lies outside of the loop, so any variables declared inside the loop are hidden from it.

It's important that any loop eventually ends, and in this code it's not completely obvious why that happens. It turns out that each time through the loop (with the possible exception of the first time), a and b get smaller. If you step through a few examples, you'll be able to convince yourself.

If the loop runs long enough, b eventually reaches 1. At that point b must evenly divide a no matter what a is so the loop ends. If b does reach 1, then 1 is the greatest common divisor of the user's original numbers and those numbers are called relatively prime.

Euclid's Algorithm

This algorithm was described by the Greek mathematician Euclid (circa 300 BC), so it's called the Euclidean algorithm or Euclid's algorithm. I don't want to explain why the algorithm works because it's complicated and irrelevant to this discussion of loops (you can find a good discussion at primes.utm.edu/glossary/xpage/EuclideanAlgorithm.html), but I do want to explain what the code does.

The code starts by storing the user's input numbers in variables a and b. It then declares variable remainder and enters a do loop.

Inside the loop, the program calculates the remainder when you divide a by b. If that value is not 0 (that is, b does not divide a evenly), then the program sets a = b and b = remainder.

Now the code reaches the end of the loop. The while statement makes the loop end if remainder is 0. When that happens, b holds the greatest common divisor.

You may want to step through the code in the debugger to see how the values change.

NOTE

A do loop always executes its code at least once because it doesn't check its condition until the end. Often that feature is why you pick a do loop over a while loop. If you might not want the loop to execute even once, use a while loop. If you need to run the loop once before you can tell whether to stop, use a do loop.

break and continue

The break and continue statements change the way a loop works.

The break statement makes the code exit the loop immediately without executing any more statements inside the loop.

For example, the following code searches the selected items in a ListBox for the value Carter. If it finds that value, it sets the boolean variable carterSelected to true and breaks out of the loop. If the ListBox has many selected items, breaking out of the loop early may let the program skip many loop iterations and save some time:

// See if Carter is one of the selected names.

bool carterSelected = false;

foreach (string name in namesListBox.SelectedItems)

{

if (name == "Carter")

{

carterSelected = true;

break;

}

}

MessageBox.Show(carterSelected.ToString());

The continue statement makes a loop jump to its looping statement early, skipping any remaining statements inside the loop after the continue statement.

For example, the following code uses a foreach loop to display the square roots of the numbers in an array. The Math.Sqrt function cannot calculate the square root of a negative number so, to avoid trouble, the code checks each value. If it finds a value less than zero, it uses the continue statement to skip the rest of that trip through the loop so it doesn't try to take the number's square root. It then continues with the next number in the array:

// Display square roots.

float[] values = { 4, 16, -1, 60, 100 };

foreach (float value in values)

{

if (value < 0) continue;

Console.WriteLine(string.Format("The square root of {0} is {1:0.00}",

value, Math.Sqrt(value)));

}

The following text shows this program's results:

The square root of 4 is 2.00

The square root of 16 is 4.00

The square root of 60 is 7.75

The square root of 100 is 10.00

NOTE

The break and continue statements make loops work in nonstandard ways and sometimes that can make the code harder to read, debug, and maintain. Use them if it makes the code easier to read, but ask yourself whether there's another simple way to write the loop that avoids these statements. For example, the following code does the same things as the previous square root code but without a continue statement:

// Display square roots.

float[] values = { 4, 16, -1, 60, 100 };

foreach (float value in values)

{

if (value >= 0)

{

Console.WriteLine(string.Format("The square root of {0} is

{1:0.00}",

value, Math.Sqrt(value)));

}

}

Try It

In this Try It, you make the simple login form shown in Figure 19.1. When the program's startup form loads, it enters a loop that makes it display this form until the user enters the correct username and password or clicks Cancel.

Screenshot of LoginForm window presenting two textboxes for Username and Password with OK and Cancel buttons at the bottom.

Figure 19.1

Lesson Requirements

In this lesson, you:

· Build a main form that displays a success message.

· Build the login dialog shown in Figure 19.1.

· In the main form's Load event handler, create an instance of the login dialog. Then enter a while loop that displays the dialog and doesn't stop until the user enters a username and password that match values in the code. If the user clicks Cancel, close the main form.

NOTE

You can download the code and resources for this lesson from the website at www.wrox.com/go/csharp24hourtrainer2e.

Hints

· Use a boolean variable named tryingToLogin to control the loop. Initialize it to true before the loop and set it to false when the user either cancels or enters the right username and password.

· To decide whether the user entered a valid username and password, compare them to the strings “User” and “Secret.” (A real application would validate these values with an encrypted database or by using some other authentication method.)

Step-by-Step

· Build a main form that displays a success message.

1. Place labels on the form to display the message.

· Build the login dialog shown in Figure 19.1.

1. Create the controls shown in Figure 19.1.

2. Set the password TextBox's PasswordChar property to X.

· In the main form's Load event handler, create an instance of the login dialog. Then enter a while loop that displays the dialog and doesn't stop until the user enters a username and password that match values in the code. If the user clicks Cancel, close the main form and break out of the loop.

1. The following code shows one possible solution:

2. // Make the user log in.

3. private void Form1_Load(object sender, EventArgs e)

4. {

5. // Create a LoginForm.

6. LoginForm frm = new LoginForm();

7. // Repeat until the user successfully logs in.

8. bool tryingToLogin = true;

9. while (tryingToLogin)

10. {

11. // Display the login dialog and check the result.

12. if (frm.ShowDialog() == DialogResult.Cancel)

13. {

14. // The user gives up. Close and exit the loop.

15. this.Close();

16. tryingToLogin = false;

17. }

18. else

19. {

20. // See if the user entered valid values.

21. if ((frm.usernameTextBox.Text == "User") &&

22. (frm.passwordTextBox.Text == "Secret"))

23. {

24. // Login succeeded. Stop trying to log in.

25. tryingToLogin = false;

26. }

27. else

28. {

29. // Login failed. Display a message and

30. // let the loop continue.

31. MessageBox.Show("Invalid username and password.");

32. }

33. }

34. }

35. // If we get here, we're done trying to log in.

}

Exercises

1. Make a program that calculates the sum 1 + 2 + 3 + … + N for a number N entered by the user.

2. [Hard] Make a program that calculates the Nth Fibonacci number for a number N entered by the user. The Fibonacci sequence is defined by:

3. Fibonacci(0) = 0

4. Fibonacci(1) = 1

Fibonacci(N) = Fibonacci(N - 1) + Fibonacci(N - 2)

Hint: Use a loop. Define variables fibo1, fibo2, and fiboN outside the loop. Inside the loop, make the variables hold Fibonacci(N - 1), Fibonacci(N - 2), and Fibonacci(N). (To test your code, Fibonacci(10) = 55 and Fibonacci(20) = 6,765.)

5. Make a program that lets the user enter test scores into a ListBox. After adding each score, display the minimum, maximum, and average values. (Hint: Before you start the loop, initialize minimum and maximum variables to the value of the first score. Then loop through the list revising the variables as needed.)

6. Copy the program you wrote for Exercise 14-1 (or download the version on the book's website) and add a List Items button. When the user clicks the button, use the Console class to display the items and their values in the Output window as a semicolon-separated list similar to the following:

7. **********

8. Pencil;$0.10;12;$1.20;

9. Pen;$0.25;12;$3.00;

10. Notebook;$1.19;3;$3.57;

**********

Hint: The ListView control's Items property is a collection of ListViewItem objects. Loop through that collection to get information about each row.

Hint: Each ListViewItem has a SubItems property that is a collection of ListViewItem.ListViewSubItem objects. For each row, loop through the item's subitem collection to get the values for that row. Use Console.Write to add data to the Console window without adding a carriage return.

11.Make a program similar to the one shown in Figure 19.2 that generates all possible four-letter words using the letters A, B, C, and D. (Hint: Make an array containing the letters A, B, C, and D. Use a foreach loop to loop through the letters. Inside that loop, use another loop to loop through the letters again. After four depths of nested loops, concatenate the looping variables to get the word.)Screenshot of LetterCombinations window displaying a list of possible four-letter combinations containing the letters A, B, C, and D. The phrase “Generated 256 words” is displayed at the bottom.

Figure 19.2

12.[Games] Copy the program you built for Exercise 18-8 (or download the version on the book's website) and modify it so it displays four bouncing balls. Hints:

· Use four PictureBox controls to hold the ball images.

· When the program starts:

§ Create class-level arrays Vx and Vy to hold the balls' velocities.

§ Create and initialize an array named Balls to hold references to the balls' PictureBoxes.

§ Loop through the Balls array and give the balls random initial locations and velocities.

· In the Timer's Tick event handler, loop through the Balls array and update the balls' locations and velocities.

· Because balls will hit the sides of the form more often than they did in Exercise 18-8, you may want to change the boing sound effect to something shorter like a click.

13.[Graphics, Games] If you look closely at the program you wrote for Exercise 6, you can see the corners of the balls' PictureBoxes when they overlap each other. Copy that program and fix it by following these steps:

· Remove the ball PictureBoxes.

· Define class-level constants NumBalls = 4, BallWidth = 40, and BallHeight = 40.

· Create X and Y arrays to hold the balls' locations. When the form loads, initialize those arrays with random positions.

· When the Timer's Tick event fires, update the balls' locations and velocities as before (except using the X and Y arrays instead of the PictureBox controls' Left and Top properties).

· After you update all of the balls' locations, call the form's Refresh method to make it redraw itself.

· Give the form the following Paint event handler to draw the balls:

· // Draw the balls.

· private void Form1_Paint(object sender, PaintEventArgs e)

· {

· e.Graphics.SmoothingMode =

· System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

· for (int ball = 0; ball < NumBalls; ball++)

· {

· e.Graphics.FillEllipse(Brushes.Red,

· X[ball], Y[ball], BallWidth, BallHeight);

· e.Graphics.DrawEllipse(Pens.Black,

· X[ball], Y[ball], BallWidth, BallHeight);

· }

}

· If you experiment with the program for a while, you'll notice some flickering. To fix that, set the form's DoubleBuffered property to True.

14.[Graphics, Games] Copy the program you wrote for Exercise 7 and modify it to give the balls random sizes and colors. Hints:

· Make an array named BallBrushes to hold Brush objects.

· In the form's Load event handler:

§ Make an array named brushes to hold Brush objects. Initialize it to a selection of standard brushes such as Brushes.Pink and Brushes.LightGreen.

§ Use code similar to the following to give each ball a brush selected randomly from the brushes array:

BallBrushes[ball] = brushes[rand.Next(0, brushes.Length)];

15.[Games, Hard] Copy the program you built for Exercise 18-14 (or download the version on the book's website) and modify it so it displays three UFOs. Hints:

· Use techniques similar to those you used in Exercise 6 to manage the UFOs' positions and velocities.

· Loop through the UFOs to see if the laser bolt has hit any of them. If it has, remove the bolt so it doesn't pass through a UFO, possibly hitting one higher up on the form.

16.[Graphics, Games] Make a worm program similar to the one shown in Figure 19.3. The program should draw a chain of circles that bounces around the form.Screenshot of Worm window displaying a chain of circles depicting a worm that bounces around the window.

Figure 19.3

Hints:

· Use a List<Point> to keep track of the positions of the circles.

· When the Timer ticks:

§ Use velocity components to calculate a new position for the first position in the list.

§ Use the list's Insert method to insert a new Point at the beginning of the list for the new position.

§ Use the list's RemoveAt method to remove the last position from the list.

§ Call the form's Refresh method to make it redraw itself.

· Make the form's Paint event handler loop through the list and draw the worm's circles.

17.[Graphics, Games, Hard] Copy the program you wrote for Exercise 10 and modify it so it displays three worms with different colors. Hints:

· Store the worms' brushes in an array of Brush objects.

· Store the lists of worm positions in an array of lists of Point objects (List<Point>[]).

· Before you use the List<Point>[], you need to initialize it with the new keyword.

· Before you use a list inside the List<Point>[], you need to initialize it with the new keyword.

18.[Games] Copy the program you built for Exercise 9. The program uses the following code to display the high scores on the HighScoreForm:

19. // Display the high scores.

20. HighScoresForm highScoresForm = new HighScoresForm();

21. highScoresForm.nameLabel0.Text = HighScoreNames[0];

22. highScoresForm.nameLabel1.Text = HighScoreNames[1];

23. highScoresForm.nameLabel2.Text = HighScoreNames[2];

24. highScoresForm.nameLabel3.Text = HighScoreNames[3];

25. highScoresForm.nameLabel4.Text = HighScoreNames[4];

26. highScoresForm.scoreLabel0.Text = HighScores[0].ToString();

27. highScoresForm.scoreLabel1.Text = HighScores[1].ToString();

28. highScoresForm.scoreLabel2.Text = HighScores[2].ToString();

29. highScoresForm.scoreLabel3.Text = HighScores[3].ToString();

highScoresForm.scoreLabel4.Text = HighScores[4].ToString();

Modify the program so it uses two for loops instead. (Hints: Use two arrays holding the form's controls. You'll have to make the change in two places.)

NOTE

Please select the videos for Lesson 19 online at www.wrox.com/go/csharp24hourtrainer2evideos.