8. Week 4 Thursday: Break, Continue, and Some Common Errors
≪ 7. Week 4 Tuesday: Practise with Loops! | Table of ContentsThere’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.
- Accept a line of text into a string variable
input
. LetN
be its length. - 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 indexi
. - If
ch
is equal to the lowercase or uppercase version ofletter
, replace it with*
and exit this loop.
- Set
- For each index
- 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
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)
.