3.5.8. The Fence Post Problem

Time: 00:03:00 | Download: Large, Large (CC), Small | Streaming, Streaming (CC) | Slides (PDF)

Loops, especially for-loops, are subject to two common issues at the problem boundaries (i.e., the beginning and end of the loop). The first error is often called off by one, while the name of the second seems entirely unrelated to programming: the fence post problem.

for (int i = 1; i < 10; i++) for (int i = 0; i <= 10; i++)
(a)(b)
for (int i = 1; i <= 10; i++) for (int i = 0; i < 10; i++)
(c)(d)
The off by one problem. Imagine that a problem requires the program to perform some operation ten times. A for-loop is the natural choice for conducting this kind of iterative task. Furthermore, the numbers generated by the loop control variable may or may not be significant - that is, the loop's body may use the control variable it may or may not.
  1. This for-loop is off by one: it begins at 1, but the last iteration occurs when i is 9 - the body of the loop does not run when i is 10. It loops or iterates nine times: 1-9.
  2. This for-loop is off by one: it begins at 0 and iterates eleven times: 0-10.
  3. This for-loop iterates ten times: 1-10.
  4. This for-loop iterates ten times: 0-9.
Programmers typically solve off-by-one problems simply by focusing on two parts of the for-loop: (1) the initialization of the loop and (2) the test that ends the loop. If the program uses the loop control variable in the loop body, then the off-by-one solution, (c) or (d), must match the overall problem the program solves.
Two posts with one span of fence between them. Three posts with two spans of fence, one span between each post. The middle post is attached to two spans, one on the left and one on the right. Four posts with three spans of fence. The two middle posts have spans on both the left and right.
The fence post problem. There is always one more fence post than there are fence spans.

wiktionary.org provides a simple, clear definition of the fence post problem:

If one wants to say "lay a fence post, then a length of fence, then repeat," then a special case must be made for the final fence post. If one wants to say "lay a length of fence, then a fence post, then repeat," then a special case must be made for the initial fence post.
[Downloaded 2019/01/12]
A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z The picture shows how the pattern of fence posts and spans relates to a similar pattern found in programming problems. Suppose we want to print the English alphabet letters separated by a '|' character but without a '|' before the first character or after the last. We can use a loop to do most of the work. The letters correspond to the fence posts, and the '|' characters correspond to the fence spans.
(a)(b)
for (char c = 'A'; c <= 'Z'; c++)
	cout << c << '|';
cout << endl;
A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|
(c)(d)
Solving the fence post problem: attempt 1. A simple fence post problem and a naive (i.e., incorrect) solution.
  1. The desired program output
  2. The output is a simple example of the fence post problem
  3. Our first, simple attempt, which is incorrect
  4. The real output of the for-loop - it has an extra '|' at the end
for (char c = 'A'; c <= 'Z'; c++)
{
	cout << c;
	if (c != 'Z')
		cout << '|';
}
cout << endl;
cout << 'A';
for (char c = 'B'; c <= 'Z'; c++)
	cout << '|' << c;
cout << endl;
for (char c = 'A'; c < 'Z'; c++)
	cout << c << '|';
cout << 'Z' << endl;
(a)(b)(c)
Solving the fence post problem: attempt 2. Several ways exist to solve a fence post problem, but some are smaller and less complex than others.
  1. There are at least three ways to solve a fence post problem with an if-statement, and this is typically the first approach a new programmer takes. However, this approach is larger, a bit more complex, and incurs the (albeit small) expense of running the if-statement during each iteration of the loop.
  2. A second approach, suggested by the second part of the wiktionary definition, is to treat the first iteration of the loop as a special case. We do this by changing the starting point of the loop, c = 'B' replaces c = 'A', and perform the first output operation before entering the loop.
  3. A third approach, suggested by the first part of the wiktionary definition and my favorite, is treating the loop's last iteration as a special case. While this solution is slightly smaller than the previous one, I like it primarily for two reasons: (1) shortening the loop by one iteration involves a small (and to me a natural) change to the loop, c <= 'Z' becomes c < 'Z' and (2) the output removed from the loop is naturally incorporated into an existing output statement. This incorporation notwithstanding, (b) and (c) are generally equivalent in size and complexity.
Although solutions to real fence post problems may be more complicated than either (b) or (c), the two code fragments form patterns we use to solve "real world" problems.