Programming Fundamentals (Python/C++)

Chapter 4
Selection and Iteration

Bachelors Program in Full Stack Development
Programming Fundamentals (Python/C++)   •   Bachelor

Selection and Iteration

Introduction

Programs rarely execute every instruction in a fixed order. They must make decisions, repeat actions, respond to changing data, and stop when a task is complete. Selection chooses among alternative paths; iteration repeats a block of statements. Together with Boolean logic, these constructs form the foundation of algorithmic control flow.

In Python and C++, selection is commonly expressed with `if`, `elif`/`else if`, and `else`. C++ also provides `switch`, while modern Python provides structural pattern matching through `match`. Iteration is expressed primarily through `while` and `for` loops. Professional programming requires more than knowing syntax: a programmer must reason about state, validation, termination, invariants, and the consequences of nested control flow.

This chapter develops those ideas and applies them to practical programming problems.

Learning Objectives

By the end of this chapter, you should be able to:

  • construct Boolean expressions using relational and logical operators;
  • design conditional statements and multiway selection structures;
  • use Python pattern matching and C++ `switch` appropriately;
  • select between `for` and `while` iteration;
  • develop nested loops, sentinel-controlled loops, and validation loops;
  • explain state changes, loop invariants, and termination conditions;
  • use `break`, `continue`, and related control-flow mechanisms responsibly;
  • identify common errors such as off-by-one errors, infinite loops, and incorrect Boolean logic;
  • analyze a complete control-flow solution to a realistic programming problem.

1. Boolean Logic as the Basis of Control Flow

A Boolean expression evaluates to either `True` or `False` in Python and to a Boolean value in C++ (`bool`). Conditions determine which statements execute.

Relational operators include:

  • equality: `==`
  • inequality: `!=`
  • less than and greater than: `<`, `>`
  • less than or equal to and greater than or equal to: `<=`, `>=`

The assignment operator `=` is not an equality test. Confusing `=` with `==` is a frequent source of errors. In C++, accidental assignment inside a condition may compile in some cases, so clear style and compiler warnings are important.

Logical operators combine conditions:

| Meaning | Python | C++ |

|---|---|---|

| conjunction | `and` | `&&` |

| disjunction | `or` | `||` |

| negation | `not` | `!` |

For example, an applicant is eligible if their age is at least 18 and they have supplied an identification number:

```python

eligible = age >= 18 and id_number != ""

```

```cpp

bool eligible = age >= 18 && idNumber != "";

```

Short-Circuit Evaluation

Logical expressions are normally evaluated from left to right and may stop as soon as their result is known. This is called short-circuit evaluation. In the following Python expression, the second part is evaluated only if `user` is not `None`:

```python

if user is not None and user.is_active:

grant_access()

```

Short-circuiting is useful for both efficiency and safety. However, conditions should remain readable; deeply complicated expressions should be divided into named Boolean variables or helper functions.

De Morgan’s Laws

Two useful transformations are:

  • `not (A and B)` is equivalent to `(not A) or (not B)`;
  • `not (A or B)` is equivalent to `(not A) and (not B)`.

These laws help when expressing the opposite of a condition. For example, the opposite of “the score is between 50 and 100 inclusive” is not simply `score < 50 and score > 100`, because that condition can never be true. The correct opposite is:

```python

score < 50 or score > 100

```

Truth Tables and Precedence

The conjunction `and`/`&&` has higher precedence than disjunction `or`/`||` in both languages. Thus:

```python

A or B and C

```

means `A or (B and C)`, not `(A or B) and C`. Parentheses should be used whenever they improve clarity or prevent misinterpretation.

A condition should also account for boundary values. For a valid percentage from 0 through 100, use `0 <= percentage <= 100` in Python, or `percentage >= 0 && percentage <= 100` in C++.

2. Selection Structures

Two-Way Selection

An `if` statement executes a block only when its condition is true. An `if-else` statement chooses exactly one of two alternatives.

```python

if temperature < 0:

state = "solid"

else:

state = "not solid"

```

```cpp

if (temperature < 0) {

state = "solid";

} else {

state = "not solid";

}

```

Indentation is syntactically significant in Python. In C++, braces delimit blocks, but consistent indentation remains essential for human readers.

Multiway Selection

Several related alternatives may be expressed using an `if`/`elif` chain in Python or an `if`/`else if` chain in C++:

```python

if mark >= 70:

grade = "A"

elif mark >= 60:

grade = "B"

elif mark >= 50:

grade = "C"

else:

grade = "F"

```

The conditions are tested from top to bottom, and the first true condition is selected. The order matters. Placing `mark >= 50` first would prevent the later categories from being reached for marks of 60 or 70.

Conditions should represent mutually exclusive cases when possible. If more than one independent action may be required, separate `if` statements may be more appropriate than an `elif` chain.

C++ `switch`

C++ `switch` is suitable when one expression is compared with several constant integral, character, or enumeration values:

```cpp

switch (command) {

case 'a':

addItem();

break;

case 'r':

removeItem();

break;

case 'q':

quit = true;

break;

default:

std::cout << "Unknown command";

}

```

The `break` statements prevent fall-through, in which execution continues into the next case. Fall-through can be intentional, but it must be documented. The `default` case handles values not explicitly listed.

`switch` is not a general replacement for `if`. It is less suitable for ranges, compound conditions, or comparisons involving strings in standard C++ switch syntax.

Python Structural Pattern Matching

Python’s `match` statement supports structural and value-based patterns:

```python

match command:

case "add":

add_item()

case "remove":

remove_item()

case "quit":

quit_program()

case _:

print("Unknown command")

```

The underscore is a wildcard pattern. Patterns can also deconstruct tuples, lists, and objects, and can include guards:

```python

match response:

case ("error", code) if code >= 500:

retry()

case ("error", code):

report(code)

case ("ok", value):

process(value)

case _:

reject()

```

Pattern matching is valuable when the structure of data matters, whereas ordinary `if` statements are often clearer for simple ranges and Boolean conditions.

3. Iteration and Repetition

Iteration repeats a block while a control condition or collection determines the repetitions. Every sound loop should answer three questions:

1. What state changes on each iteration?

2. What condition ends the loop?

3. Why must the loop eventually terminate?

`while` Loops

A `while` loop is appropriate when the number of repetitions is unknown or controlled by an event, input, or sentinel.

```python

attempts = 0

while attempts < 3:

try_login()

attempts += 1

```

```cpp

int attempts = 0;

while (attempts < 3) {

tryLogin();

++attempts;

}

```

The loop condition is tested before each iteration. Therefore, the body may execute zero times. The update must move the state toward termination; omitting `attempts += 1` creates an infinite loop unless another exit is reached.

`for` Loops

A `for` loop is appropriate when traversing a collection or processing a known sequence of values.

```python

for item in items:

process(item)

```

```cpp

for (const auto& item : items) {

process(item);

}

```

Python’s `range(start, stop, step)` includes `start` but excludes `stop`. For example, `range(1, 5)` produces 1, 2, 3, and 4. This half-open convention makes the number of values equal to `stop - start` when the step is 1 and reduces boundary errors.

C++ also supports a traditional counted loop:

```cpp

for (int i = 0; i < 5; ++i) {

std::cout << i << '\n';

}

```

The three parts are initialization, continuation condition, and update. The condition `i < 5` produces five iterations with `i` equal to 0 through 4.

Sentinel-Controlled Iteration

A sentinel is a special value that signals the end of input but is not processed as ordinary data.

```python

total = 0

value = int(input("Enter a value, or -1 to finish: "))

while value != -1:

total += value

value = int(input("Enter a value, or -1 to finish: "))

```

The sentinel must be distinguishable from valid data. If `-1` is a legitimate transaction amount, another termination mechanism is needed, such as a separate command or end-of-file. The input must be read before the first condition test and again at the end of each iteration; this is sometimes called the priming read pattern.

Validation Loops

Validation repeats input until it satisfies a stated rule:

```python

while True:

age = int(input("Age from 0 to 120: "))

if 0 <= age <= 120:

break

print("Invalid age")

```

A robust system validates both type and range. The example assumes the user enters text that can be converted to an integer. Production code should also handle conversion errors, for example with `try`/`except` in Python or input-state checks in C++.

4. Nesting and Multidimensional Control Flow

A control structure may contain another control structure. A nested loop is useful for tables, grids, combinations, and repeated searches.

```python

for row in range(3):

for column in range(4):

print(f"({row}, {column})", end=" ")

print()

```

The outer loop executes three times and the inner loop executes four times for each outer iteration, so the body executes 12 times. This multiplication of work is important when considering performance: a loop of size `n` nested inside a loop of size `m` generally performs approximately `m × n` body executions, excluding early exits and other costs.

Nested conditionals should preserve a clear relationship between the condition and the action. Excessive nesting often signals that a function should be decomposed, a guard clause introduced, or a data-driven design used.

`break` and `continue`

`break` immediately exits the nearest loop. `continue` skips the remaining statements in the current iteration and begins the next iteration.

```python

for value in values:

if value is None:

continue

if value == target:

found = True

break

```

These mechanisms can make searches and filtering concise, but excessive use can obscure the normal path of execution. In nested loops, `break` exits only the innermost loop. A flag, helper function, or clearly defined return may be required to exit multiple levels.

C++ also supports `goto`, but it should generally be avoided in ordinary application code because uncontrolled jumps make state and reasoning difficult. Structured control flow is usually safer and more maintainable.

5. State, Invariants, and Termination

Program State

The state of a program is the information relevant to its current behavior: variable values, collection contents, input position, and object status. A loop transforms one state into another. For example, in a running total, the state includes both the next input value and the accumulated total.

Loop Invariants

A loop invariant is a property that remains true at a specified point in every iteration. Invariants support systematic reasoning about correctness.

Consider:

```python

total = 0

for value in values:

total += value

```

An invariant at the beginning of each iteration is:

> `total` equals the sum of all values processed so far.

Initially, no values have been processed and `total` is zero, so the invariant holds. During an iteration, adding the current value preserves it. When the loop ends, every value has been processed; therefore `total` is the sum of the complete collection.

This reasoning has three parts:

  • Initialization: the invariant is true before the first iteration;
  • Maintenance: one iteration preserves the invariant;
  • Termination: when the loop stops, the invariant implies the desired result.

Termination and Progress

A loop terminates when its condition eventually becomes false or an explicit exit occurs. A practical termination argument identifies a quantity that moves toward a boundary, such as a counter increasing toward a limit or a remaining collection becoming smaller.

For a loop such as:

```python

i = 0

while i < n:

process(i)

i += 1

```

assuming `n` is fixed and finite, `i` increases by one each iteration. It therefore reaches `n`, making `i < n` false. If `i` is never updated, termination cannot be established.

Termination arguments must also consider exceptional paths. Input validation that rejects invalid values but fails to request new input can produce an infinite loop. A loop that waits for a resource should have a timeout or cancellation strategy where appropriate.

6. Common Control-Flow Errors

Off-by-One Errors

These occur when a loop executes one time too many or too few. Compare:

```python

for i in range(len(items)):

print(items[i])

```

with a loop that accesses `items[i + 1]`. The latter requires `i` to stop before the final index. Prefer direct iteration when an index is unnecessary:

```python

for item in items:

print(item)

```

Incorrect Conditions

Errors often result from using `and` where `or` is required. To reject a score outside 0–100, use:

```python

if score < 0 or score > 100:

print("Invalid")

```

Using `and` would describe an impossible condition for ordinary numeric values.

Assignment Versus Comparison

Use `=` for assignment and `==` for comparison. In Python, `if x = 3:` is a syntax error. In C++, it may be accepted in some contexts, which makes compiler warnings and deliberate comparison style especially important.

Missing Updates and Accidental Infinite Loops

Every `while` loop should make its progress visible. If an update is hidden in a complex function, reviewers may not be able to verify termination. Keep control variables local and document intentional infinite loops, such as event-processing loops that terminate through `break` or program shutdown.

Dangling `else` and Indentation Problems

In nested C++ conditionals, an `else` associates with the nearest unmatched `if`. Braces eliminate ambiguity. In Python, incorrect indentation changes the program structure or causes an error. Consistent formatting is a correctness practice, not merely a style preference.

Mutable Collection Modification

Removing elements from a collection while iterating over it can skip elements or invalidate iterators. Safer approaches include creating a filtered collection, iterating over a copy, or using language-supported removal algorithms.

7. A Systematic Design Method

A reliable control-flow design can follow these steps:

1. State the problem and valid input precisely.

2. Identify the program state that must be maintained.

3. Decide whether the task needs selection, iteration, or both.

4. Choose a loop based on the source of repetition: collection, count, condition, or sentinel.

5. Write the termination condition before writing the loop body.

6. Specify an invariant or progress measure.

7. Handle invalid, empty, and boundary cases.

8. Test typical, minimum, maximum, and unusual inputs.

9. Refactor complicated conditions into named functions or variables.

This method separates algorithmic reasoning from syntax and reduces defects.

8. Applied Case: Validating and Summarizing Assessment Results

The following is an illustrative case study. A department wants a console program that reads assessment scores, rejects invalid scores, stops when the user enters `-1`, and reports the count, average, highest score, and pass count. Scores are valid integers from 0 through 100; `-1` is reserved as the sentinel.

Background and Problem

Manual spreadsheet entry has produced invalid scores and inconsistent summaries. The program must accept an arbitrary number of scores, validate each score, and avoid treating the sentinel as an assessment result.

Analysis

The state consists of:

  • `count`: number of valid scores processed;
  • `total`: sum of valid scores;
  • `highest`: greatest valid score seen so far;
  • `passes`: number of valid scores at least 50.

The sentinel `-1` ends the input. Values below `-1` or above 100 are invalid and must not change the summary.

An invariant after processing any number of valid scores is:

  • `count` equals the number of valid scores processed;
  • `total` equals their sum;
  • if `count > 0`, `highest` is their maximum;
  • `passes` equals the number of those scores that are at least 50.

Solution in Python

```python

count = 0

total = 0

passes = 0

highest = None

while True:

text = input("Enter score (0-100), or -1 to finish: ")

try:

score = int(text)

except ValueError:

print("Enter a whole number.")

continue

if score == -1:

break

if score < 0 or score > 100:

print("Score must be between 0 and 100.")

continue

count += 1

total += score

passes += score >= 50

if highest is None or score > highest:

highest = score

if count == 0:

print("No scores were entered.")

else:

average = total / count

print(f"Count: {count}")

print(f"Average: {average:.2f}")

print(f"Highest: {highest}")

print(f"Passes: {passes}")

```

In Python, `score >= 50` is a Boolean value, and adding it to an integer works because `True` behaves numerically as 1 and `False` as 0. Although valid Python, some teams may prefer the more explicit form `if score >= 50: passes += 1` for readability.

Correctness and Outcome

The sentinel is tested before validation, so it is not included in the statistics. Invalid scores trigger `continue`, leaving the summary state unchanged. Every valid score updates all relevant state variables exactly once. The `break` provides termination when the user supplies `-1`.

If the valid inputs are 80, 45, and 70, followed by `-1`, then `count` is 3, `total` is 195, `highest` is 80, `passes` is 2, and the average is 65.00. The empty-input case is handled separately to avoid division by zero and the undefined notion of a highest score.

Learning Points

This case demonstrates validation, a sentinel, a priming-free `while True` structure with explicit exits, state invariants, an empty-case guard, and the separation of invalid input from valid processing. It also illustrates why control flow must be designed around requirements rather than assembled from isolated syntax examples.

9. Professional Practice, Testing, and Contemporary Tools

Control flow affects reliability, security, accessibility, and maintainability. Validation should occur at trust boundaries because user input, files, and network data may be malformed or deliberately adversarial. A loop that fails to limit retries can enable denial-of-service behavior; a selection condition that incorrectly grants authorization can create a serious security defect.

Unit tests should exercise each branch and important boundary: an empty collection, the smallest valid value, the largest valid value, just-outside values, repeated values, and termination input. Code coverage is useful but does not prove correctness; tests must also check expected behavior and invariants.

Linters, static analyzers, compiler warnings, formatters, debuggers, and integrated development environments can detect unreachable branches, suspicious assignments, missing `break` statements, and possible infinite loops. AI coding assistants can suggest conditions or tests, but generated control flow must be reviewed. AI may misunderstand sentinel meanings, omit boundary cases, or produce a loop that appears plausible but does not terminate. Human reasoning about requirements, invariants, and tests remains essential.

Good practice includes using meaningful names, limiting nesting, preferring direct iteration, making ranges explicit, documenting intentional fall-through, avoiding duplicated conditions, and extracting complex decisions into well-named functions. These practices improve reviewability and reduce the cognitive cost of maintaining software.

Chapter Summary

Selection and iteration are the central mechanisms by which programs respond to data and repeat work. Boolean expressions determine decisions, and relational and logical operators must be combined with careful attention to precedence, boundaries, and short-circuit behavior. `if` chains suit ranges and compound conditions; C++ `switch` and Python `match` suit discrete alternatives and structured patterns.

`for` loops are effective for collections and known sequences, while `while` loops suit unknown repetition, validation, and sentinel-controlled input. Nested loops require attention to execution cost and clarity. `break` and `continue` are useful but should not obscure the normal control path. Correct loops require explicit state changes, a termination argument, and, where appropriate, a loop invariant. Common defects include off-by-one errors, incorrect Boolean expressions, assignment in place of comparison, missing updates, accidental fall-through, and unsafe collection modification.

Professional control-flow programming combines syntax with reasoning. Requirements, validation, boundary testing, static analysis, and human review are necessary for dependable software, especially when programs process untrusted input or make consequential decisions.

Key Terms

Boolean expression: An expression whose result represents true or false.

Branch: One possible path through a selection structure.

Condition: A Boolean expression used to control execution.

Control flow: The order in which program statements execute.

Continue: A statement that skips to the next loop iteration.

Fall-through: Execution continuing from one switch case into another.

For loop: An iteration structure commonly used for sequences or counted repetition.

Guard clause: An early condition that handles an exceptional or invalid case.

Invariant: A property that remains true at a defined point in every iteration.

Iteration: One execution of a loop body.

Loop: A control structure that repeats statements.

Match: Python’s structural pattern-matching statement.

Nested loop: A loop contained inside another loop.

Sentinel: A special value used to signal termination of input.

Selection: Choosing among alternative execution paths.

Short-circuit evaluation: Stopping Boolean evaluation once the result is known.

State: Values and information describing a program’s current situation.

Termination condition: A condition that causes repetition to stop.

Validation: Checking whether data satisfies specified requirements.

Review Questions

Short Answer Questions

1. What is the difference between an `if`/`elif` chain and several independent `if` statements?

2. Why is the sentinel value excluded from a sentinel-controlled calculation?

3. What is short-circuit evaluation, and how can it improve safety?

4. State the three elements of a basic loop-correctness argument.

5. What is the purpose of `break` in a C++ `switch` statement?

Long Answer Questions

1. Compare `for` and `while` loops in Python and C++, giving appropriate use cases for each.

2. Explain loop invariants using a running-total loop as an example.

3. Describe Python `match` and C++ `switch`, including their strengths and limitations.

4. Explain at least four common control-flow errors and methods for preventing them.

5. Design a validation loop for an integer menu choice from 1 through 4 and explain its termination behavior.

Analytical / Application Questions

1. A loop reads integers until `0` is entered, but zero is also a valid transaction amount. Analyze why this design is ambiguous and propose two alternatives.

2. Given a nested loop that processes 500 rows and 20 columns, determine how many cell-processing iterations occur if no early exit is used. Explain how the design might change for very large data sets.

3. Review a program that accepts scores from 0 to 100, uses `-1` as a sentinel, and reports an average. Identify the state variables, write a suitable invariant, and describe tests for invalid input, empty input, and boundary scores.

Multiple Choice Questions with Answers

Question 1

Which operator tests equality in both Python and C++?

A. =

B. ==

C. !=

D. <=

Correct Answer: B. ==

Question 2

What does Python range(2, 6) produce?

A. 2, 3, 4, 5

B. 2, 3, 4, 5, 6

C. 1, 2, 3, 4, 5

D. 3, 4, 5, 6

Correct Answer: A. 2, 3, 4, 5

Question 3

Which loop is generally most appropriate for processing every element of a collection?

A. A switch statement

B. A for loop

C. A single if statement

D. A break statement

Correct Answer: B. A for loop

Question 4

What is the principal purpose of a sentinel?

A. To increase a counter

B. To validate every input as numeric

C. To signal that input processing should end

D. To select a switch case

Correct Answer: C. To signal that input processing should end

Question 5

In a C++ switch statement, what commonly prevents execution from continuing into the next case?

A. continue

B. default

C. return type

D. break

Correct Answer: D. break

Question 6

Which condition correctly identifies a score outside the inclusive range 0 through 100?

A. score < 0 and score > 100

B. score >= 0 and score <= 100

C. score < 0 or score > 100

D. score == 0 or score == 100

Correct Answer: C. score < 0 or score > 100

Question 7

What does continue do inside a loop?

A. Exits all nested loops

B. Skips the rest of the current iteration

C. Repeats the current statement indefinitely

D. Terminates the program

Correct Answer: B. Skips the rest of the current iteration

Question 8

Which property is a suitable invariant for a running-total loop?

A. The total equals the sum of values processed so far

B. The total is always greater than every input value

C. The loop variable never changes

D. The collection is always empty

Correct Answer: A. The total equals the sum of values processed so far

Question 9

A nested loop runs 3 times in its outer loop and 4 times in its inner loop for every outer iteration. How many times does the inner body execute?

A. 7

B. 12

C. 16

D. 1

Correct Answer: B. 12

Question 10

Why must an empty-input case be handled before calculating an average?

A. Because addition is impossible

B. Because the sentinel must be counted

C. Because division by zero would occur

D. Because loops cannot process integers

Correct Answer: C. Because division by zero would occur

Programming Fundamentals (Python/C++)
Chapter 4 of 15