🔒
Pro Content
Subscribe to access all 69 OCR H446 A Level lessons.
£7.99/month
or £59/year
Subscribe now →
🔒 Pro · Component 2 · 2.1.1 Computational Thinking
2.1.1b Thinking Ahead, Procedurally, Logically and Concurrently
OCR H446 · A Level Computer Science · ~18 min read
Notes
Video
Slides
Worksheet
Quiz

Thinking Ahead

Thinking ahead means identifying the inputs, outputs, and pre-conditions of a problem before writing any code. A programmer who thinks ahead considers: what data will the program receive? What should it produce? What conditions must hold before the algorithm can run correctly? What might go wrong?

Preconditions

A precondition is a condition that must be true before a function or algorithm is called for it to produce correct results. Example: a binary search precondition is that the list must already be sorted. If the precondition is violated, the function's behaviour is undefined.

Pre-conditions simplify functions by letting them assume input is already in the expected state. They are part of good software specification — they form a contract between the caller and the function.

Caching and Prefetching

Caching is thinking ahead about what data will be needed next and storing a pre-computed or pre-fetched copy so it is ready immediately. CPU caches store frequently-used memory addresses. Web browsers cache downloaded resources. Memoisation in programming caches the results of expensive function calls.

Prefetching fetches data into cache before it is explicitly requested, based on prediction of what will be needed next. A CPU uses branch prediction to prefetch instructions it thinks will follow a branch.

Reusable Components

Thinking ahead also means designing code for reuse: writing functions, modules, libraries, and classes that are general enough to be used in multiple contexts. This requires planning the interface (parameters and return types) before writing the implementation. It avoids code duplication (DRY — Don't Repeat Yourself).

Thinking Procedurally

Thinking procedurally means identifying and ordering the steps required to solve a problem, then identifying which steps can be expressed as sub-procedures and combining them into a complete solution. It is the basis of procedural (imperative) programming.

Key aspects of procedural thinking

1. Identify the sequence of steps needed. 2. Identify which steps can be reused (→ create sub-procedures). 3. Identify which steps need to be repeated (→ loops). 4. Identify which steps are conditional (→ selection/branching). 5. Order the steps correctly — output depends on correct sequencing.

Procedural thinking maps directly to structured programming concepts: sequence (steps in order), selection (if/else/case), iteration (loops), and sub-routines (procedures and functions).

Identifying Sub-procedures

A key skill is recognising when a sequence of steps is a coherent, reusable sub-task and extracting it into a named procedure. Benefits: readability (main program reads like an outline), reusability (call the same procedure from many places), testability (test each procedure independently), maintainability.

Thinking Logically

Thinking logically means identifying the conditions that determine which path through a program to take, and expressing those conditions precisely using Boolean logic. It requires being exact about what conditions must be true for each branch to execute.

Conditions and Decisions

For each decision point in an algorithm, a programmer thinking logically asks: what must be true for branch A vs. branch B? Are the conditions mutually exclusive? Do they cover all cases? Are there edge cases?

Boolean Logic in Decision Making

Conditions use Boolean operators: AND (both must be true), OR (at least one must be true), NOT (negation). Thinking logically ensures conditions are precise, non-overlapping, and cover all possible input states (exhaustive).

Proof and Verification

Thinking logically also includes trace tables (manually executing an algorithm with test data to verify correctness), dry runs, and using formal logic to prove an algorithm is correct. For exam purposes: identify conditions, express them as Boolean expressions, and verify with test cases.

Exam tip: "Thinking logically" questions often ask about conditions. Be precise. Don't write "if the number is big" — write "if n > 100". Always check: are your conditions exhaustive (cover all cases)? Are they mutually exclusive (no overlap)?

Thinking Concurrently

Thinking concurrently means identifying parts of a problem that can be solved at the same time (in parallel), rather than sequentially (one after another). This is important in multi-core processors, distributed systems, and multi-threaded applications.

Concurrent vs Sequential

SequentialConcurrent
Steps run one at a timeMultiple steps run simultaneously
Simple to reason aboutHarder to reason about (race conditions, deadlocks)
Limited by single-core speedLimited by number of parallel units
No data sharing issuesShared data requires synchronisation
Suitable for dependent stepsOnly for independent sub-problems

Identifying Concurrent Tasks

Not all problems can be parallelised. A task can be run concurrently only if it is independent of other concurrent tasks — it does not need their results as input, and they do not share mutable state.

Example: rendering the frames of a video independently. Frame 50 does not depend on frame 49's render completing first — they can be rendered on different CPU/GPU cores simultaneously.

Counter-example: summing a list sequentially. Each addition depends on the previous result. However, the list can be split and sub-sums computed in parallel, then combined — this is the parallel reduction pattern.

Problems with Concurrency

  • Race condition: Two threads read/write shared data in a non-deterministic order, leading to incorrect results depending on timing.
  • Deadlock: Two or more threads wait for each other to release a resource — neither can proceed.
  • Starvation: A thread is perpetually denied the resources it needs because other threads keep getting priority.

Solutions include mutexes (mutual exclusion locks), semaphores, and designing algorithms to avoid shared mutable state.

Concurrency in Real Systems

  • Multi-core CPUs: Run multiple threads simultaneously on different cores
  • GPU computing: Thousands of cores run the same operation on different data (SIMD — Single Instruction, Multiple Data)
  • Distributed computing: Different machines run different parts of a computation (MapReduce, Hadoop)
  • Pipelining: CPU overlaps instruction stages (fetch, decode, execute, write-back) so multiple instructions are in different stages simultaneously
Exam tip: When asked "which parts of this problem can be done concurrently?", identify tasks that are independent — they don't share data and don't depend on each other's output. Explain WHY they can run concurrently (no dependencies, no shared state).
Exam tip: Know all four thinking modes: Thinking Ahead = preconditions, caching, reuse; Procedurally = steps and sub-procedures; Logically = conditions and decisions; Concurrently = parallel independent tasks.
✓ Notes completed!
Video coming soon
Click to advance · Arrow keys also work
Click slide or press arrow keys to navigate

Worksheet — 2.1.1b Thinking Ahead, Procedurally, Logically & Concurrently

8 questions · 24 marks · instantly marked

Q1Explain what is meant by "thinking ahead" in computational thinking and give two specific examples.[4 marks]
✓ Mark scheme
Thinking ahead means identifying inputs, outputs, and pre-conditions before writing code, and planning for future needs [1]. Examples (any 2 from): defining a precondition for a function, e.g. "list must be sorted" before binary search [1]; designing reusable/general functions so they can be called from multiple places [1]; caching results of expensive computations so they're available immediately next time [1]; prefetching data predicted to be needed before it's requested [1].
Q2What is a precondition? Give an example and explain the benefit of using preconditions.[4 marks]
✓ Mark scheme
A precondition is a condition that must be true before a function or algorithm is called for it to produce correct results [1]. Example: binary search requires the list to be sorted [1]. Benefit 1: simplifies function implementation — it can assume input is in the required state without adding validation code [1]. Benefit 2: forms a contract between the caller and the function, making the interface clearer and easier to reason about [1].
Q3Explain "thinking procedurally". Describe the four key constructs it maps to in structured programming.[5 marks]
✓ Mark scheme
Thinking procedurally means identifying and ordering the steps to solve a problem, then grouping reusable steps into sub-procedures [1]. Four constructs: Sequence — steps executed one after another in a defined order [1]; Selection — conditional branching (if/else/case), executing different steps depending on conditions [1]; Iteration — repeating steps in a loop while or until a condition is met [1]; Sub-routines (procedures/functions) — named, reusable blocks of steps called from the main program and elsewhere [1].
Q4Explain "thinking logically". Why must conditions in an algorithm be both exhaustive and mutually exclusive?[4 marks]
✓ Mark scheme
Thinking logically means identifying precise conditions that determine which path through a program to take, expressed as Boolean expressions [1]. Exhaustive: conditions must cover all possible cases — if an input doesn't match any condition, the algorithm has no defined behaviour (bug) [1]. Mutually exclusive: conditions must not overlap — if an input matches two conditions, it is undefined which branch will execute, causing non-deterministic behaviour [1]. Together they guarantee correct, predictable behaviour for all valid inputs [1].
Q5Explain what concurrency means and give one real-world computing example where it is beneficial.[3 marks]
✓ Mark scheme
Concurrency means identifying and running independent sub-tasks simultaneously rather than sequentially, to reduce total time [1]. Example (any 1): GPU rendering different pixels/frames simultaneously on thousands of cores [1]; multi-core CPU running separate program threads simultaneously [1]; distributed systems processing different data records on different servers in parallel [1]; CPU instruction pipelining overlapping fetch-decode-execute stages [1].
Q6Explain the difference between a race condition and a deadlock. Give a brief example of each.[4 marks]
✓ Mark scheme
Race condition: two threads access/modify shared data simultaneously in a non-deterministic order, giving incorrect results depending on timing [1]. Example: two threads both read a counter (value=5), both add 1, both write 6 — the counter should be 7 [1]. Deadlock: two or more threads each hold a resource the other needs, and each waits for the other to release it — neither can proceed [1]. Example: Thread A holds File X and waits for File Y; Thread B holds File Y and waits for File X [1].
Q7A task involves: (A) sorting a list, (B) calculating the average of the sorted list, (C) finding the minimum and maximum. State which tasks can run concurrently, which must run sequentially, and explain why.[3 marks]
✓ Mark scheme
Sorting (A) must complete before averaging (B), because B needs the sorted list as input — they are dependent, so must run sequentially [1]. Finding the average (B) and finding the min/max (C) can run concurrently if the list is already sorted, because neither depends on the other's result — they are independent sub-problems that can be computed simultaneously [1]. However, if the minimum/maximum is derived from the sorted list, C can also start as soon as sorting is done (C and B concurrently); if C works on the unsorted list, C can run concurrently with A too [1]. (Accept any logically consistent analysis with justification.)
Q8Explain how caching relates to thinking ahead and why it improves performance.[3 marks]
✓ Mark scheme
Caching relates to thinking ahead because it anticipates what data will be needed next and stores it close to the processor/user before it is requested [1]. It improves performance because retrieving data from cache is much faster than recomputing it or fetching it from main memory/disk/network [1]. Example: memoisation caches the result of an expensive recursive function call; a CPU L1 cache stores recently-accessed memory addresses; a web browser caches downloaded images [1].
Topic Quiz
1 of 15
You scored
out of 15
🎯

Mini Test — 2.1.1b Four Thinking Modes

  • 10 questions · 10 marks · 10 minutes
  • 5 MCQ + 5 short answer
Card 1 of 15
Click to reveal
🎉
Complete!
TermDefinition
← 2.1.1a Abstraction & Decomposition 2.1.1 Computational Thinking Next: 2.2.1a Programming Techniques →