Skip to content

Coding with conditions

Logic and puzzles · level 4, lesson 5 of 6

A question to start

A museum's ticket machine runs this program. It asks for your age, then:

if age < 13
    price is 3 dollars
else if age < 5
    price is 0 dollars
else if age >= 65
    price is 4 dollars
else
    price is 8 dollars

The museum wants children under 5 to get in free. A family arrives with a 3-year-old, and the machine charges 3 dollars. Nobody under 5 ever gets in free. Why? The fix needs no new lines, only a change of order. Can you see it?

The idea

Conditions are statements

In level 3 code ran straight down, or round and round in loops. Now code makes choices. A choice depends on a condition: a statement that is either true or false when the program reaches it, like age < 13 or n is even. Conditions are exactly the statements of lesson 1, and they can be joined with and, or and not using the same truth tables.

If

if temperature < 0
    say "wear a hat"

If the condition is true, the indented lines (the body) run. If it is false, they are skipped. Either way the program then carries on below.

If ... else

if n mod 2 = 0
    say "even"
else
    say "odd"

Here exactly one of the two bodies runs: the first if the condition is true, the second if it is false. They can never both run, and one of them always runs. That is the logic of a statement and its negation: exactly one is true.

n mod 2 means "the remainder when \(n\) is divided by 2". An even number leaves remainder 0; an odd number leaves remainder 1. So n mod 2 = 0 is the computer's way of asking "is \(n\) even?". (More on remainders in Number theory.)

A chain: if ... else if ... else

if score >= 90
    grade is "A"
else if score >= 75
    grade is "B"
else if score >= 50
    grade is "C"
else
    grade is "try again"

The computer checks the conditions from the top, and runs the body of the first one that is true. Then it skips all the rest of the chain. If none is true, the else body runs.

So a score of 95 gets "A". It is also at least 75 and at least 50, but those lines are never reached. Order matters!

That explains the ticket machine. A 3-year-old reaches age < 13 first. It is true, so the price is 3 dollars, and the age < 5 line is never checked. In fact age < 5 can never be reached as true: anyone under 5 is also under 13 and is caught first. The fix is to check the narrower condition first:

if age < 5
    price is 0 dollars
else if age < 13
    price is 3 dollars
else if age >= 65
    price is 4 dollars
else
    price is 8 dollars

Nested conditions

A condition can sit inside the body of another one. This is called nesting, just like loops inside loops.

if a > b
    if a > c
        biggest is a
    else
        biggest is c
else
    if b > c
        biggest is b
    else
        biggest is c

This finds the biggest of three numbers \(a\), \(b\), \(c\) (when they are all different). The outer if decides between \(a\) and \(b\); the inner one compares the winner with \(c\).

Tracing by hand

To check code, trace it: pick some input values, follow the lines one by one, and write down what each condition gives (T or F) and which line runs.

Trace the biggest-number code with \(a = 4\), \(b = 9\), \(c = 7\):

Line Condition Value What happens
if a > b \(4 > 9\) F go to the outer else
if b > c \(9 > 7\) T biggest is \(b\), which is 9

Testing the edges

The best inputs to test are the ones at the edges, where a condition just changes from true to false: an age of exactly 5, 13 or 65, a score of exactly 90. Many bugs hide there. Is a 13-year-old a child? With age < 13, no. If the museum meant "13 and under", the code should say age <= 13.

Worked examples

1. Classifying triangles by their sides. Given three lengths \(a\), \(b\), \(c\), say whether they make an equilateral, isosceles or scalene triangle, or no triangle at all.

First, not every three lengths make a triangle. Sides 2, 3 and 7 cannot meet: the two short sides together (5) do not reach across the long one (7). The rule is that each side must be shorter than the other two added together. (This is the triangle inequality; more in Geometry.)

if a >= b + c or b >= a + c or c >= a + b
    say "not a triangle"
else if a = b and b = c
    say "equilateral"
else if a = b or b = c or a = c
    say "isosceles"
else
    say "scalene"

Trace it with three inputs.

  • \(5, 5, 5\): the first condition is F (\(5 \ge 10\) is false, three times). The second is T. Equilateral.
  • \(3, 5, 3\): the first is F (\(3 \ge 8\), \(5 \ge 6\), \(3 \ge 8\) are all false). The second is F (\(a = b\) is false). The third: \(a = c\) is true, so the or is T. Isosceles.
  • \(4, 4, 8\): the first: \(c \ge a + b\) is \(8 \ge 8\), true. Not a triangle (the shape would be squashed flat).

Why must "equilateral" come before "isosceles"? An equilateral triangle also passes the isosceles test (it has two equal sides, and more). If the isosceles line came first, \(5, 5, 5\) would be called isosceles, and the equilateral line could never be reached: the same bug as the ticket machine.

2. Leap years. The calendar rule is: a year is a leap year if it is divisible by 4, except years divisible by 100, which are not, except years divisible by 400, which are. As one condition:

if year mod 4 = 0 and (year mod 100 ≠ 0 or year mod 400 = 0)
    say "leap year"
else
    say "not a leap year"

Trace it.

Year mod 4 = 0 mod 100 ≠ 0 mod 400 = 0 bracket (or) whole (and)
2024 T T F T leap
2023 F T F T not leap
1900 T F F F not leap
2000 T F T T leap

The brackets matter: they say the or is worked out first, then the and, just as in a truth table.

3. A loop with a condition inside. Count from 1 to 15. For multiples of 3 say "Fizz", for multiples of 5 say "Buzz", for multiples of both say "FizzBuzz", and otherwise say the number.

repeat for n from 1 to 15
    if n mod 15 = 0
        say "FizzBuzz"
    else if n mod 3 = 0
        say "Fizz"
    else if n mod 5 = 0
        say "Buzz"
    else
        say n

It says: 1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz.

The mod 15 test must come first. A number divisible by both 3 and 5 is divisible by 15 (because 3 and 5 share no factor). If the mod 3 line came first, 15 would just say "Fizz".

Try it

  1. What does this code say when \(n = 7\)? When \(n = 12\)?

    if n mod 2 = 0
        say "even"
    else
        say "odd"
    
  2. Trace the grade code above for scores of 75, 74 and 50. Which grade does each get?

  3. Write code that says "positive", "negative" or "zero" for a number \(x\).
  4. Trace the biggest-number code with \(a = 8\), \(b = 3\), \(c = 10\). Which lines run?
  5. Use the triangle code: what does it say for (a) \(6, 6, 11\); (b) \(3, 4, 5\); (c) \(2, 3, 7\)?
  6. Use the leap-year condition to decide whether 2100 and 2400 are leap years. Show the T and F values.
  7. Find the bug: this code should say "teen" for ages 13 to 19.

    if age > 13 and age < 19
        say "teen"
    
  8. The biggest-number code assumes \(a\), \(b\), \(c\) are all different. What does it say for \(a = 5\), \(b = 5\), \(c = 2\)? Is the answer still correct?

Answers
  1. \(n = 7\): \(7 \bmod 2 = 1\), so the condition is F and it says "odd". \(n = 12\): \(12 \bmod 2 = 0\), so it says "even".
  2. 75: \(75 \ge 90\) is F, \(75 \ge 75\) is T, so "B". 74: F, F (\(74 \ge 75\) is false), then \(74 \ge 50\) is T, so "C". 50: F, F, then \(50 \ge 50\) is T, so "C".
  3. One way:

    if x > 0
        say "positive"
    else if x < 0
        say "negative"
    else
        say "zero"
    

    If both tests fail, \(x\) is not more than 0 and not less than 0, so it must be 0.

  4. if a > b: \(8 > 3\) is T, so go into the first part. if a > c: \(8 > 10\) is F, so the inner else runs: biggest is \(c\), 10.

  5. (a) \(6 + 6 = 12 > 11\) and the others are fine, so it is a triangle; \(a = b\) but \(b \ne c\), so not equilateral; \(a = b\), so isosceles. (b) \(3 + 4 = 7 > 5\), so a triangle; no two sides equal, so scalene (it is the right triangle from the last lesson). (c) \(2 + 3 = 5\), and \(7 \ge 5\), so not a triangle.
  6. 2100: mod 4 = 0 is T; mod 100 ≠ 0 is F; mod 400 = 0 is F (2100 is not a multiple of 400); the bracket is F, so the whole is F: not a leap year. 2400: T; F; T (2400 = 6 × 400); the bracket is T, so the whole is T: leap year.
  7. The edges are wrong. age > 13 is false for 13, and age < 19 is false for 19, so 13- and 19-year-olds are not called teens. Fix: if age >= 13 and age <= 19.
  8. if a > b: \(5 > 5\) is F, so go to the outer else. if b > c: \(5 > 2\) is T, so biggest is \(b\), which is 5. The answer 5 is still correct (both \(a\) and \(b\) are biggest). It is worth checking such "equal" cases: they are edges too.

Common mistake

Putting a wide condition before a narrow one. In an else if chain, only the first true condition counts. If a wider test (age < 13, isosceles, mod 3) comes before a narrower one inside it (age < 5, equilateral, mod 15), the narrower one is never reached. Put the most special case first.

Getting the edges wrong. < and <= differ in exactly one value, and that value is usually the one in the question ("13 and over", "at least 50"). Test the edge on purpose.

Where it leads