• No results found

Printing Prime Numbers

In document C++ (Page 148-162)

6.5 Iteration Examples

6.5.2 Printing Prime Numbers

A prime number is an integer greater than one whose only factors (also called divisors) are one and itself. For example, 29 is a prime number (only 1 and 29 divide into it with no remainder), but 28 is not (2, 4, 7, and 14 are factors of 28). Prime numbers were once merely an intellectual curiosity of mathematicians, but now they play an important role in cryptography and computer security.

The task is to write a program that displays all the prime numbers up to a value entered by the user. Listing 6.21 (printprimes.cpp) provides one solution.

Listing 6.21: printprimes.cpp 1 #include <iostream> 2 3 using namespace std; 4 5 int main() 6 { 7 8 int max_value;

9 cout << "Display primes up to what value? "; 10 cin >> max_value;

11 int value = 2; // Smallest prime number

12 while ( value <= max_value )

13 {

14 // See if value is prime

15 bool is_prime = true; // Provisionally, value is prime

16 // Try all possible factors from 2 to value - 1

17 int trial_factor = 2;

18 while ( trial_factor < value )

19 {

20 if ( value % trial_factor == 0 )

21 {

22 is_prime = false; // Found a factor

23 break; // No need to continue; it is NOT prime

24 }

25 trial_factor++;

26 }

27 if ( is_prime )

28 cout << value << " "; // Display the prime number

29 value++; // Try the next potential prime number

30 }

31 cout << endl; // Move cursor down to next line

32 }

6.5. ITERATION EXAMPLES 138

Display primes up to what value? 90

2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89

The logic of Listing 6.21 (printprimes.cpp) is a little more complex than that of Listing 6.20 (startree.cpp). The user provides a value for max_value. The main loop (outerwhileiterates over all the values from two to max_value:

• Two new variables, local to the body of the outer loop, are introduced: trial_factor and

is_prime. is_prime is initialized to true, meaning value is assumed to be prime unless our tests prove otherwise. trial_factor takes on all the values from two to value - 1 in the inner loop:

int trial_factor = 2;

while ( trial_factor < value ) {

if ( value % trial_factor == 0 ) {

is_prime = false; // Found a factor

break; // No need to continue; it is NOT prime }

trial_factor++; }

The expression value % trial_factor is zero when trial_factor divides into value with no remainder—exactly when trial_factor is a factor of value. If any of the values of

trial_factoris determined to actually be a factor of value, then is_prime is set to false, and the loop is exited via thebreak. If the loop continues to completion, is_prime will never be set to false, which means no factors were found and value is indeed prime.

• Theifstatement after the inner loop: if ( is_prime )

cout << value << " "; // Display the prime number

simply checks the status of is_prime. If is_prime is true, then value must be prime, so value is printed along with an extra space to separate from other factors that may be printed during the next iterations.

Some important questions can be asked. 1. If the user enters a 2, will it be printed?

In this case max_value = value = 2, so the condition of the outer loop

value <= max_value

is true, since 2 <= 2. is_prime is set to true, but the condition of the inner loop

trial_factor < value

is not true (2 is not less than 2). Thus, the inner loop is skipped, is_prime is not changed from true, and 2 is printed. This behavior is correct, because 2 is the smallest prime number (and the only even prime).

6.5. ITERATION EXAMPLES 139

2. if the user enters a number less than 2, is anything printed?

Thewhilecondition ensures that values less than two are not considered. The body of thewhile will never be entered. Only the newline is printed, and no numbers are displayed. This behavior is correct.

3. Is the inner loop guaranteed to always terminate?

In order to enter the body of the inner loop, trial_factor must be less than value. value does not change anywhere in the loop. trial_factor is not modified anywhere in the if statement within the loop, and it is incremented within the loop immediately after the if state- ment. trial_factor is, therefore, incremented during each iteration of the loop. Eventually,

trial_factorwill equal value, and the loop will terminate. 4. Is the outer loop guaranteed to always terminate?

In order to enter the body of the outer loop, value must be less than or equal to max_value.

max_valuedoes not change anywhere in the loop. value is increased in the last statement within the body of the outer loop, and value is not modified anywhere else. Since the inner loop is guaran- teed to terminate as shown in the previous answer, eventually value will exceed max_value and the loop will end.

The logic of the innerwhilecan be rearranged slightly to avoid thebreakstatement. The current version is:

while ( trial_factor < value ) {

if ( value % trial_factor == 0 ) {

is_prime = false; // Found a factor

break; // No need to continue; it is NOT prime }

trial_factor++; }

It can be rewritten as:

while ( is_prime && trial_factor < value ) {

is_prime = (value % trial_factor != 0); trial_factor++; // Try next factor }

This version without thebreakintroduces a slightly more complicated condition for thewhilebut re- moves theifstatement within its body. is_prime is initialized to true before the loop. Each time through the loop it is reassigned. trial_factor will become false if at any time value % trial_factor is zero. This is exactly when trial_factor is a factor of value. If is_prime becomes false, the loop cannot continue, and if is_prime never becomes false, the loop ends when trial_factor becomes equal to value. Because of operator precedence, the parentheses are not necessary. The parentheses do improve readability, since an expression including both == and != is awkward for humans to parse. When parentheses are placed where they are not needed, as in

x = (y + 2);

6.6. SUMMARY 140

This loop can be further shortened:

while ( is_prime && trial_factor < value ) is_prime = (value % trial_factor++ != 0);

This version uses the post-increment operator within the test expression (see Section 4.8). Recall that with the post-increment operator the value of the variable is used in the surrounding expression (if any), and then the variable is incremented. Since thewhile’s body now contains only one statement, the curly braces are not needed.

6.6

Summary

• Thewhilestatement allows the execution of code sections to be repeated multiple times. • The condition of thewhilecontrols the execution of statements within thewhile’s body.

• The statements within the body of awhileare executed over and over until the condition of the whileis false.

• If thewhile’s condition is initially false, the body is not executed at all. • In an infinite loop, thewhile’s condition never becomes false.

• The statements within thewhile’s body must eventually lead to the condition being false; otherwise, the loop will be infinite.

• Do not confusewhilestatements withifstatements; their structure is very similar (whilere- served word instead of theifword), but they behave differently.

• Infinite loops are rarely intentional and are usually accidental.

• An infinite loop can be diagnosed by putting a printing statement inside its body.

• An assignment expression has a value; the expression’s value is the same as the expression on the right side of the assignment operator. This fact can be used to streamline the the control of a loop that repeats based on user input.

• A loop contained within another loop is called a nested loop.

• Iteration is a powerful mechanism and can be used to solve many interesting problems.

• A block is any section of source code enclosed within curly braces. A compound statement is one example of a block.

• A variable declared within a block is local to that block.

• Complex iteration using nested loops mixed with conditional statements can be difficult to do cor- rectly.

• Sometimes simple optimizations can speed up considerably the execution of loops.

• Thebreakstatement immediately exits a loop, skipping the rest of the loop’s body, without checking to see if the condition is true or false. Execution continues with the statement immediately following the body of the loop.

6.7. EXERCISES 141

• In a nested loop, thebreakstatement exits only the loop in which thebreakis found.

• Thegotostatement directs the program’s execution to a labeled statement within the function. The gotostatement is legitimately used only to exit completely from the depths of a nested loop. • Thecontinuestatement immediately checks the loop’s condition, skipping the rest of the loop’s

body. If the condition is true, the execution continues at the top of the loop as usual; otherwise, the loop is terminated and execution continues with the statement immediately following the loop’s body. false.

• In a nested loop, thecontinuestatement affects only the loop in which thecontinueis found.

6.7

Exercises

1. In Listing 6.4 (addnonnegatives.cpp) could the condition of theifstatement have used > instead of

>=and achieved the same results? Why?

2. In Listing 6.4 (addnonnegatives.cpp) could the condition of thewhilestatement have used > instead of >= and achieved the same results? Why?

3. Use a loop to rewrite the following code fragment so that it uses just one cout and one endl.

cout << 2 << endl; cout << 4 << endl; cout << 6 << endl; cout << 8 << endl; cout << 10 << endl; cout << 12 << endl; cout << 14 << endl; cout << 16 << endl;

4. In Listing 6.4 (addnonnegatives.cpp) what would happen if the statement containing cin is moved out of the loop? Is moving the assignment out of the loop a good or bad thing to do? Why?

5. How many asterisks does the following code fragment print? int a = 0; while ( a < 100 ) { cout << "*"; a++; } cout << endl;

6. How many asterisks does the following code fragment print? int a = 0;

while ( a < 100 ) cout << "*"; cout << endl;

6.7. EXERCISES 142 int a = 0; while ( a > 100 ) { cout << "*"; a++; } cout << endl;

8. How many asterisks does the following code fragment print? int a = 0; while ( a < 100 ) { int b = 0; while ( b < 55 ) { cout << "*"; b++; } cout << endl; }

9. How many asterisks does the following code fragment print? int a = 0; while ( a < 100 ) { if ( a % 5 == 0 ) cout << "*"; a++; } cout << endl;

10. How many asterisks does the following code fragment print? int a = 0; while ( a < 100 ) { int b = 0; while ( b < 40 ) { if ( (a + b) % 2 == 0 ) cout << "*"; b++; } cout << endl; a++; }

6.7. EXERCISES 143 int a = 0; while ( a < 100 ) { int b = 0; while ( b < 100 ) { int c = 0; while ( c < 100 ) { cout << "*"; c++; } b++; } a++; } cout << endl;

12. What is printed by the following code fragment? int a = 0;

while ( a < 100 ) cout << a++; cout << endl;

13. What is printed by the following code fragment? int a = 0;

while ( a > 100 ) cout << a++; cout << endl;

14. Rewrite the following code fragment using abreakstatement and eliminating the done variable. Your code should behave identically to this code fragment.

bool done = false;

int n = 0, m = 100;

while ( !done && n != m ) { cin >> n; if ( n < 0 ) done = true; cout << "n = " << endl; }

15. Rewrite the following code fragment so it does not use abreakstatement. Your code should behave identically to this code fragment.

// Code with break ...

16. Rewrite the following code fragment so it eliminates thecontinuestatement. Your new code’s logic should be simpler than the logic of this fragment.

6.7. EXERCISES 144 int x = 100, y; while ( x > 0 ) { cin >> y; if ( y == 25 ) { x--; continue; } cin >> x; cout << "x = " << x << endl; }

17. Suppose you were given some code from the 1960s in a language that did not support structured statements likewhile. Your task is to modernize it and adapt it to C++. The following code fragment has been adapted to C++already, but you must now structure it with awhilestatement to replace thegotos. Your code should begotofree and still behave identically to this code fragment.

int i = 0; top: if ( i >= 10 ) goto end; cout << i << endl; i++; goto top; end:

18. Simplify withgoto...

19. What is printed by the following code fragment? int a = 0;

while ( a < 100 ); cout << a++; cout << endl;

20. Write a C++program that accepts a single integer value entered by the user. If the value entered is less than one, the program prints nothing. If the user enters a positive integer, n, the program prints an n × n box drawn with * characters. If the users enters 1, for example, the program prints

*

If the user enters a 2, it prints

** **

6.7. EXERCISES 145

*** *** ***

and so forth. If the user enters 7, it prints

******* ******* ******* ******* ******* ******* *******

that is, a 7 × 7 box of * symbols.

21. Write a C++ program that allows the user to enter exactly twenty double-precision floating-point values. The program then prints the sum, average (arithmetic mean), maximum, and minimum of the values entered.

22. Write a C++ program that allows the user to enter any number of non-negative double-precision floating-point values. The user terminates the input list with any negative value. The program then prints the sum, average (arithmetic mean), maximum, and minimum of the values entered. The terminating negative value is not used in the computations.

23. Redesign Listing 6.20 (startree.cpp) so that it draws a sideways tree pointing right; for example, if the user enters 7, the program would print

* ** *** **** ***** ****** ******* ****** ***** **** *** ** *

24. Redesign Listing 6.20 (startree.cpp) so that it draws a sideways tree pointing left; for example, if the user enters 7, the program would print

6.7. EXERCISES 146 * ** *** **** ***** ****** ******* ****** ***** **** *** ** *

147

Chapter 7

Other Conditional and Iterative

Statements

Theif/elseandwhilestatements are sufficient to implement any algorithms that involve conditional execution and looping. Thebreakandcontinuestatements are convenient but are not necessary. C++ provides some additional conditional and iterative statements that are more convenient to use in some circumstances. These additional statements include

switch: an alternative to some multi-wayif/elsestatements

• the conditional operator: an expression that exhibits the behavior of anif/elsestatement • do/while: a loop that checks its condition after its body is executed

for: a loop convenient for counting

These alternate constructs allow certain parts of algorithms to expressed more clearly and succinctly. This chapter explores these other forms of expressing conditional execution and iteration.

7.1

The switch Statement

Theswitchstatement provides a convenient alternative for some multi-wayif/elsestatements like the one in Listing 5.13 (restyleddigittoword.cpp). Listing 7.1 (switchdigittoword.cpp) is a new implementation of Listing 5.13 (restyleddigittoword.cpp) that uses aswitchstatement instead of a multi-wayif/else statement. Listing 7.1: switchdigittoword.cpp 1 #include <iostream> 2 3 using namespace std; 4 5 int main() 6 { 7 int value;

7.1. THE SWITCH STATEMENT 148

8 cout << "Please enter an integer in the range 0...5: "; 9 cin >> value; 10 switch ( value ) 11 { 12 case 0: 13 cout << "zero"; 14 break; 15 case 1: 16 cout << "one"; 17 break; 18 case 2: 19 cout << "two"; 20 break; 21 case 3: 22 cout << "three"; 23 break; 24 case 4: 25 cout << "four"; 26 break; 27 case 5: 28 cout << "five"; 29 break; 30 default: 31 if ( value < 0 )

32 cout << "Too small";

33 else

34 cout << "Too large";

35 break;

36 }

37 cout << endl;

38 }

7.1. THE SWITCH STATEMENT 149

switch

(

integral expression )

{

case

integral constant1

:

statement(s)

break;

case

integral constant2

:

statement(s)

break;

case

integral constant3

:

statement(s)

break;

.

.

.

case

integral constantn

:

statement(s)

break;

default:

statement(s)

break;

}

In aswitchstatement

• The reserved wordswitchidentifies aswitchstatement.

• The required parenthesized expression that follows the wordswitchmust evaluate to an integral value. Any integer type, characters, and Boolean expressions are acceptable. Floating point expres- sions and other non-integer types are forbidden.

• The body of theswitchis enclosed by required curly braces.

• Each occurrence of the wordcaseis followed by an integral constant and a colon (:). We call the integral constant acaselabel. This label can be either a literal value or aconstsymbolic value (see Section 3.5). In particular, non-constvariables and other expressions are expressly forbidden. The case label defines a position within the code; it is not an executable statement. A case label represents a target to which the program’s execution flow can jump.

If thecaselabel matches theswitch’s expression, then the statements that follow that label are executed up until thebreakstatement is encountered. The statements andbreakstatement that follow eachcaselabel are optional. One way to execute one set of statements for more than one caselabel is to provide empty statements for one or more of the labels, as in:

7.1. THE SWITCH STATEMENT 150

cin >> key; // get key from user

switch ( key ) {

case 'p':

case 'P':

cout << "You choose \"P\"" << endl;

break; case 'q': case 'Q': done = true; break; }

Here either an upper- or lowercase P result in the same action— You chose P is printed. If the user enters either an upper- or lowercase Q, the done Boolean variable is set to true. If the user enters neither P nor Q, none of the statements in theswitchis executed.

The breakstatement is optional. When acaselabel is matched, the statements that follow are executed until abreakstatement is encountered. The control flow then transfers out of the body of theswitch. In this way, thebreakwithin aswitchworks just like abreakwithin a loop: the rest of the body of the statement is skipped and program execution resumes at the next statement fol- lowing the body. A missingbreakstatement, a common error, when its omission is not intentional, causes the statements of the succeedingcaselabel to be executed. The process continues until a breakis encountered or the end of theswitchbody is reached.

• Thedefaultlabel is matched if none of thecaselabels match. It serves as a catch all option like the finalelsein a multi-wayif/elsestatement. Thedefaultlabel is optional. If it is missing and none of thecaselabels match the expression, then no statement within theswitch’s body is executed.

Theswitchstatement has two restrictions that make it less general than the multi-wayif/else: • Theswitchargument must be an integral expression.

• Case labels must be constant integral values. Integral literals and constants are acceptable. Variables or expressions are not allowed.

To illustrate these restrictions, consider the followingif/elsestatement that translates easily to an equiv- alentswitchstatement:

if ( x == 1 ) { // Do 1 stuff here . . . } else if ( x == 2 ) { // Do 2 stuff here . . . } else if ( x == 3 ) { // Do 3 stuff here . . . }

In document C++ (Page 148-162)