Hunter Liu's Website

8. Week 4 Thursday: Break, Continue, and Some Common Errors

≪ 7. Week 4 Tuesday: Practise with Loops! | Table of Contents

There’s a slight gap in our looping skills. A lot of turn-based video games rely heavily on loops. They might be set up as, “While the player has more than 0 health…”, “While the player still has cards in the deck…”, etc. However, one may imagine that players can forfeit the game, pass their turn, or play cards that modify the flow of the game.

A specific example may be Texas Hold’em, which has some fairly complicated logic in how each round is structured. Players that fold their hand early on shouldn’t be asked to bet in the same round, but they can still play in the next. In other words, some players are skipped over while others play, and handling this with a loop can become quite delicate.

In a more abstract setting, we want the ability to skip over parts of a loop or exit a loop altogether, and this is granted to us by the break and continue statements. Both of these function exactly the same way in for and while loops.

Whenever the statement break is encountered, C++ immediately jumps right after the closing curly braces } for whatever loop you’re in. Whenever the statement continue is encountered, C++ instead jumps right before the closing }. Here’s an example to demonstrate:

 1for(int i = 1; i <= 5; i++) {
 2    if(i == 3) { 
 3        continue;
 4    } 
 5
 6    cout << i << " "; 
 7} 
 8cout << endl; 
 9
10for(int i = 1; i <= 5; i++) {
11    if(i == 3) {
12        break; 
13    } 
14
15    cout << i << " "; 
16} 
17cout << endl; 

This snippet of code prints 1 2 4 5 on one line and 1 2 on the next.

In the first loop, in the first two iterations when i = 1 and i = 2, the if statement is skipped altogether. But when i = 3, the if statement is run, and the continue skips over the cout and puts us right before the } on line 7. The loop “continues” from there, moving on to i = 4 and i = 5.

In the second loop, again the first two iterations run per usual. But when i = 3, the break in the if statementputs us right outside of the for loop, after the } on line 16. So the last two iterations are not run, and we only see 1 2 on the screen.

Here’s an example of a problem where we can apply one of these statements:

Example 1.

Write a C++ program that accepts a line of text as input, then censors the first occurrence of each letter of the alphabet.

SAMPLE RUN 
INPUT   Good morning gentlemen. 
OUTPUT  **o* *o***ng g*n**emen.

Let’s write some pseudocode for this. We should do this one letter at a time, starting from the letter a and working up to the letter z. For each of these letters, we should replace the first occurrence of either the lowercase or the uppercase form of the letter.

To do this replacement, we should scan every character of the string, front to back, replacing any matches. But we don’t want to replace multiple occurrences of any given character. If we see a match, we should just stop.

  1. Accept a line of text into a string variable input. Let N be its length.
  2. For each letter letter = a, b, ...., z, repeat the following:
    • For each index i = 0, 1, ..., N - 1, repeat the following:
      • Set ch to the character at index i.
      • If ch is equal to the lowercase or uppercase version of letter, replace it with * and exit this loop.
  3. Print the modified input to the screen.

This can be done using the find function for strings, but this involves checking a bunch of cases: one needs to handle the presence of both, one, or none of the upper-case and lower-case variant separately. Plus, the point of this is to demonstrate how to use break.

Here’s the code for this:

 1// 1. obtain user input
 2string input; 
 3getline(cin, input); 
 4int N = input.length(); 
 5
 6// 2. Repeat something for each letter a, ..., z! 
 7for(char letter = 'a'; letter <= 'z'; letter++) {
 8    // loop through the indices of the string. 
 9    for(int i = 0; i < N; i++) {
10        // get the current character and compare it to letter 
11        char ch = input.at(i); 
12        if(ch == letter || ch == letter + 'A' - 'a') {
13            input.at(i) = '*'; 
14
15            // this exits the INNER loop, 
16            // but not the OUTER loop
17            break; 
18        } 
19    } // end of inner for loop 
20} // end of outer for loop 
21
22// 3. print the results 
23cout << input << endl; 

Challenge 2.

Rewrite the above program in a way that uses neither the break command nor the find function.

Some Common Errors to Catch

Let’s pivot our attention to understanding how to catch and fix common logical and runtime errors that may arise when you’re coding with loops. Here are a handful of problems that involve one or more errors of different types. Try to identify what the error is, classify it, and give a fix.

Problem 3. Censorship

This program intends to obtain a line of input from the user, then convert all digits into asterisks. For instance, if the input was abc123, the intended output is abc***.

 1#include <iostream>
 2#include <string> 
 3
 4using namespace std; 
 5
 6int main() {
 7    // obtain input from the user 
 8    string input; 
 9    getline(cin, input); 
10
11    // for each character in the string, test if the 
12    // character is a digit (between '0' and '9'). 
13    // if it is, change it to a '*'. 
14    for(int i = 1; i <= input.length(); i++) {
15        char current_char = input.at(i); 
16        if('0' <= current_char && current_char <= '9') {
17            current_char = '*'; 
18        }
19    }
20
21    // print out the modified input 
22    cout << input << endl; 
23    return 0; 
24}

Solution

The code does compile, but upon running it, none of the digits in any inputs actually get censored out. This happens because the variable current_char is a copy of the character at index i in the string input. Changes made to current_char are not reflected in the original string!

The fix is simply to replace line 17 with input.at(i) = '*'.

Problem 4. Off by one

The following input accepts two string inputs from the user, each without any spaces. Then, it determines if the two string inputs differ by at most one character. For instance, “Hello” and “Hallo” are close, and “PIC10A” and “pic10a” are not. For this problem, “Hello” and “ello” are considered different.

 1#include <iostream> 
 2
 3using namespace std; 
 4
 5int main() {
 6    // get two string inputs from the user 
 7    string first, second; 
 8    cin >> first >> second; 
 9
10    // if the two strings have different lengths, 
11    // they cannot be the same. exit immediately. 
12    if(first.length() != second.length()) {
13        cout << "Different" << endl; 
14        return 0; 
15    }
16
17    // keep track of how many differences there are 
18    int different = 0; 
19
20    // count the number of different characters 
21    for(int i = 1; i <= first.length(); i++) { 
22        if(first.at(i) = second.at(i)) {
23            different++; 
24        }
25    }
26
27    // if the # of differences is <= 1, we are good 
28    // otherwise, they are different. 
29    if(different <= 1) {
30        cout << "Close" << endl; 
31    } else {
32        cout << "Different" << endl; 
33    }
34
35    return 0; 
36}
Challenge
The variable different computes a version of the Hamming distance between two strings. Can you modify the code so that it works for strings of different lengths as well? For instance, “Hello”, “ello”, and “Hllo” are all a “distance” of 1 character apart.

Solution

This code does not compile, as it uses string variables without including the string library.

After inserting this line and running the code, we get a runtime error. This is caused by the off-by-one error in the for loop on line 21: the range of indices includes the length of the string, which is not a valid index! The fix is to make index start at 0 and replace the <= with a <.

After making this fix, there are no longer any runtime errors, but it thinks every pair of words is different, even if the two inputs are identical! This should lead you to analyse what’s happening to the variable different and when it’s getting incremented.

Closer inspection of line 22 shows the problem: there’s only one equal sign instead of two! The condition should be first.at(i) == second.at(i).