You are viewing a free preview of this lesson.
Subscribe to unlock all 10 lessons in this course and every other course on LearningBro.
Thinking logically is the computational-thinking skill of reasoning precisely about decisions. Every program of any interest contains points where it must choose: take this branch or that one, keep looping or stop, handle the error or carry on. At each of those points the program evaluates a condition — a Boolean expression that is either true or false — and the answer determines what happens next. To think logically is to identify those decision points, pin down the exact conditions that govern them, and reason rigorously about how the conditions combine to drive the program's behaviour. It is the difference between hoping your code is right and being able to prove what it will do for any given input.
This lesson develops the H446 view: identifying decision points and the conditions attached to them, understanding how Boolean conditions determine flow of control, combining conditions with the logical operators AND/OR/NOT, and using logical reasoning — especially the trace table — to predict and check program behaviour and to locate bugs. Because every condition ultimately reduces to a Boolean value, logical thinking is also the doorway to the Boolean-algebra module, where the same operators are studied as a formal system.
This lesson addresses H446 section 2.1.4 — Thinking Logically. The specification expects you to:
Logical thinking underpins all algorithm and program design: the conditions you reason about here are the guards on loops in 2.3, the branch conditions whose Boolean structure is formalised in the Boolean-algebra module (1.4.3), and the predicates that determine state transitions in finite-state machines (2.1.6).
At its core, logical thinking is reasoning about Boolean conditions and how they steer a program.
| Concept | Definition |
|---|---|
| Decision point | A place in an algorithm where the next step depends on a condition |
| Condition | A Boolean expression that evaluates to true or false |
| Flow of control | The order in which statements execute, which conditions can alter |
| Logical reasoning | Using the conditions and rules to determine exactly what will happen |
The key insight is that a program's behaviour is completely determined by its conditions and the values flowing through them. If you know the inputs and you evaluate every condition correctly, you can predict the output with certainty — no need to run the code. That predictive power is what makes logical thinking both an analysis tool (working out what code does) and a debugging tool (working out why it does the wrong thing).
It is worth distinguishing two flavours of logical reasoning you will use. Deductive reasoning works forwards: given the inputs and the conditions, you deduce the output — this is what a trace table does. Diagnostic reasoning works backwards: given a wrong output, you reason back to which condition must have been evaluated incorrectly, or which value must have been unexpected — this is what debugging does. Both rest on the same foundation: a precise understanding of what each condition tests and how the true/false results steer the flow. A student who can only run code and observe is at the mercy of whatever inputs they happen to try; a student who can reason about the conditions can predict behaviour for inputs they have never run and explain failures they have only seen once. That is the practical payoff of thinking logically, and it is why examiners test it directly with trace tables and "explain the error" questions rather than only with "write the code" tasks.
A decision point is anywhere the single thread of execution could go more than one way. They arise from a small, fixed set of control structures:
| Structure | The decision it makes | Example |
|---|---|---|
| IF…THEN…ELSE | Choose between two branches | IF age >= 18 THEN grant ELSE deny |
| WHILE loop | Repeat, or exit | WHILE attempts < 3 DO ... |
| FOR loop | Implicit decision each pass | FOR i = 0 TO n-1 (checks i <= n-1) |
| CASE / SWITCH | Choose among many branches | CASE grade OF 'A','B',... |
| Exception handling | Handle an error, or proceed | TRY ... CATCH file-not-found |
Worked example — an ATM withdrawal. Walking through the process, every "Is…?" is a decision point with a condition behind it:
| Step | Action / decision |
|---|---|
| 1 | Customer inserts card |
| 2 | Decision: is the card valid? |
| 3 | Customer enters PIN |
| 4 | Decision: is the PIN correct? |
| 5 | Decision: have the maximum attempts been exceeded? |
| 6 | Customer selects an amount |
| 7 | Decision: is the amount ≤ the account balance? |
| 8 | Decision: does the machine hold enough cash? |
| 9 | Dispense cash |
| 10 | Update the balance |
Identifying the decision points is the first half of logical thinking; stating the condition behind each is the second. Notice too that the order of these decisions matters: the PIN is only checked after the card is found valid, and the cash-availability check comes after the balance check — a structure that mirrors the dependency reasoning of procedural thinking, since each later decision often only makes sense once an earlier one has passed.
For every decision point you must write the exact Boolean condition being tested — vagueness here is where logic errors are born.
| Decision | Condition | Kind |
|---|---|---|
| Customer is an adult | age >= 18 | integer comparison |
| PIN is correct | entered == stored | equality |
| List is empty | length(list) == 0 | integer comparison |
| Record found | result != NULL | null check |
| Temperature is dangerous | temp > 100 OR temp < -20 | compound Boolean |
Most interesting decisions are compound — several simple conditions joined by the logical operators:
| Operator | Truth rule | Example | True when |
|---|---|---|---|
| AND | true only if both sides are true | age >= 18 AND hasID | both hold |
| OR | true if either side is true | isStudent OR isStaff | at least one holds |
| NOT | inverts a Boolean | NOT fileExists | the file is absent |
The truth tables for these operators are exactly those studied formally in the Boolean-algebra module — logical thinking is where you use them, that module is where you prove things about them. A pass rule combining three conditions, (exam >= 40) AND (coursework >= 30) AND (attendance >= 80), is true only when all three hold; swap the ANDs for ORs by mistake and a student who attended but failed everything would "pass". Choosing the right operator is the heart of getting a condition correct.
When a condition has several operators, evaluating it correctly means respecting operator precedence: NOT binds most tightly, then AND, then OR — just as multiplication binds before addition in arithmetic. So a OR b AND c means a OR (b AND c), not (a OR b) AND c. These two readings can give different answers: with a = true, b = false, c = false, the intended a OR (b AND c) is true OR false = true, whereas the mistaken (a OR b) AND c is true AND false = false. Because this is such a fertile source of subtle bugs, experienced programmers add explicit brackets even when precedence would make them unnecessary, so that the intended grouping is unmistakable to a human reader. Logical reasoning about a condition therefore starts by being sure you have parsed it the way the machine will — bracket first, evaluate second.
The reason conditions matter so much is that they are the steering wheel of a program. Selection picks a branch based on a condition; iteration repeats a block while a condition holds. Trace how the Booleans drive this small routine that classifies a number:
def classify(n):
if n > 0:
if n % 2 == 0:
return "positive even"
else:
return "positive odd"
elif n == 0:
return "zero"
else:
return "negative"
The path taken is fixed entirely by the conditions: classify(6) evaluates 6 > 0 (true), then 6 % 2 == 0 (true), returning "positive even" — no other branch is even considered. Short-circuit evaluation is a subtlety worth knowing here: in A AND B, if A is false the language need not evaluate B at all (the result is already false); in A OR B, if A is true, B is skipped. This is not only a performance trick — it is often relied upon for safety, as in if items != NULL AND items.length > 0, where the left operand guards against evaluating the right one on a null reference. Reasoning about flow therefore means reasoning not just about whether each condition is true, but sometimes about which conditions even get evaluated.
To make compound evaluation concrete, work out (age >= 18 AND hasLicence) OR isInstructor for a learner driver aged 19 with no licence, accompanied by an instructor — age = 19, hasLicence = false, isInstructor = false for this person — driving with a separate instructor flag set elsewhere. Evaluate innermost first, exactly as the machine does:
| Step | Sub-expression | Result |
|---|---|---|
| 1 | age >= 18 (19 >= 18) | true |
| 2 | hasLicence | false |
| 3 | (age >= 18 AND hasLicence) | true AND false = false |
| 4 | isInstructor | false |
| 5 | ... OR isInstructor | false OR false = false |
So the whole condition is false — this driver may not drive unsupervised, which is the correct real-world answer. The discipline on display is to evaluate the bracketed innermost part first, carry its single true/false result up, and combine — never trying to judge the whole expression in one leap. Any compound condition, however long, yields to this innermost-first, one-operator-at-a-time evaluation, and writing the steps out is what turns a guess into a certainty.
The single most important logical-reasoning technique at A-Level is the trace table (or dry run): you step through an algorithm by hand, recording the value of every variable after each statement and the result of every condition. Done carefully it predicts the output with certainty and exposes exactly where a wrong program goes astray.
Consider this OCR-style pseudocode:
x = 5
y = 3
WHILE x > 0
IF x MOD 2 = 1 THEN
y = y + x
ELSE
y = y - 1
ENDIF
x = x - 1
ENDWHILE
OUTPUT y
The trace records the condition tested and every variable as the loop turns:
| Iteration | x at start | x > 0? | x MOD 2 = 1? | y after | x after |
|---|---|---|---|---|---|
| 1 | 5 | true | true (odd) | 3 + 5 = 8 | 4 |
| 2 | 4 | true | false (even) | 8 − 1 = 7 | 3 |
| 3 | 3 | true | true (odd) | 7 + 3 = 10 | 2 |
| 4 | 2 | true | false (even) | 10 − 1 = 9 | 1 |
| 5 | 1 | true | true (odd) | 9 + 1 = 10 | 0 |
| — | 0 | false → exit | — | — | — |
The loop condition x > 0 is now false, so the loop ends and the program outputs 10. Notice how the table makes both conditions explicit at every step — the loop guard and the inner IF — which is exactly what an examiner expects and what makes a slip immediately visible.
Exam Tip: Trace tables are a staple of OCR H446 papers. Give every variable its own column, add a column for each condition you must evaluate, and write one row per iteration. Showing the condition results — not just the variable values — is where the method earns its marks and catches its bugs.
When a program misbehaves, logical reasoning is the systematic way to find the fault — far more reliable than randomly changing lines.
| Step | What you do |
|---|---|
| 1 | Characterise the symptom — wrong output? crash? infinite loop? |
| 2 | Localise — which procedure or region is responsible? |
| 3 | Trace — dry-run that region with the failing input |
| 4 | Check the conditions — are the comparisons and operators correct? |
| 5 | Check the data — do the variables hold the values you expected? |
| 6 | Fix and re-test — correct it and verify with several inputs, including edge cases |
Subscribe to continue reading
Get full access to this lesson and all 10 lessons in this course.