Skip to the page
Year 11 Β· Computer Science Β· Spring 1 Β· Paper 2 practice: algorithms, programming, logic, databases

Writing pseudocode to a specification

Exam practicePresent β–Ά

What this lesson covers

7.4 Standard methods of solution

Cambridge IGCSE Computer Science 0478 Β· Topic 7: Algorithm design and problem-solving Β· Paper 2

7.5 Validation and verification

Cambridge IGCSE Computer Science 0478 Β· Topic 7: Algorithm design and problem-solving Β· Paper 2

8.1 Programming concepts

Cambridge IGCSE Computer Science 0478 Β· Topic 8: Programming Β· Paper 2

You will learn to

  • Write an algorithm in Cambridge pseudocode from a written description.
  • Use the exact keywords: DECLARE, INPUT, OUTPUT, IF, CASE, FOR, WHILE and REPEAT.
  • Use totalling, counting and a validation loop in your algorithms.
  • Use meaningful names, comments and indentation.

Success criteria

  • I can write four short algorithms that match their descriptions.
  • I can close every structure with the right keyword, like ENDIF or NEXT.
  • I can highlight the parts of my answer that match the mark-scheme points.
The syllabus points for these sections
  • Write totalling and counting in pseudocode and Python.
  • Find the maximum, minimum and average of a set of values.
  • Write and trace a linear search.
  • Write and trace a bubble sort.
  • Count the comparisons a bubble sort makes.
  • Explain the difference between validation and verification.
  • Describe and write range, length, type, presence, format and check digit checks.
  • Describe visual checks and double entry checks.
  • Write a validation loop that repeats until the input is acceptable.
  • Declare variables and constants and choose the right data type.
  • Use input, output, sequence, selection (IF and CASE) and the three types of loop.
  • Use totalling, counting, string handling and arithmetic, relational and logical operators.
  • Write procedures and functions with parameters, and explain local and global scope.
  • Use the library routines MOD, DIV, ROUND and RANDOM, and write maintainable programs.

Key words

Say each word, then read what it means. Every one is in the glossary too.

Program development life cycle
The four stages of making a program: analysis, design, coding and testing.
Pseudocode
A structured, code-like way of writing an algorithm that is not tied to one language.
Totalling
Keeping a running total by adding each value to a total variable.
Counting
Adding 1 to a counter variable each time something happens.
Linear search
Checking each item in a list in turn until the target is found or the list ends.
Bubble sort
Sorting by comparing neighbouring items and swapping them if they are in the wrong order, pass after pass.
Validation
An automatic check that input data is reasonable and follows set rules.
Verification
A check that data has been copied or typed in exactly as the original.
Check digit
An extra digit calculated from the other digits, used to catch typing errors.
Normal data
Test data that should be accepted.
Boundary data
The largest and smallest accepted values and the nearest rejected values next to them.
Trace table
A table used in a dry run to record the value of each variable as an algorithm runs.
Variable
A named place in memory whose value can change while the program runs.
Constant
A named value that is set once and cannot change while the program runs.
Identifier
The name given to a variable, constant, array or subroutine.
Data type
The kind of value stored: INTEGER, REAL, CHAR, STRING or BOOLEAN.
Assignment
Storing a value in a variable, written with ← in pseudocode.
Sequence
Statements run one after another in the order they are written.
Selection
Choosing which statements to run using a condition (IF or CASE).
Iteration
Repeating a block of statements in a loop.
Count-controlled loop
A FOR loop that repeats a set number of times.
Pre-condition loop
A WHILE loop that checks its condition before each pass, so it may run zero times.
Post-condition loop
A REPEAT UNTIL loop that checks its condition after each pass, so it runs at least once.
Procedure
A named subroutine that does a task and is run with CALL; it does not return a value.
Function
A named subroutine that returns a single value to where it was called.
Parameter
A variable in a subroutine heading that receives a value when the subroutine is called.
Local variable
A variable declared inside a subroutine that can only be used inside it.
Global variable
A variable declared in the main program that can be used anywhere in the program.
Library routine
A ready-made, tested subroutine provided with the language, such as ROUND or RANDOM.
Array
A data structure holding many values of the same data type under one name, each found by its index.

The explanation

Read it here, or open the study page below to go one step at a time with things to try.

7.4 Standard methods of solution

Totalling and counting

Totalling means adding values into a running total: `Total ← Total + Value`.

Counting means adding 1 each time something happens: `Count ← Count + 1`.

Both variables must start at 0 before the loop.

e.g.Example
Marks 34, 48, 21, 40, 15, 45. Pass mark 25. Total: 0 β†’ 34 β†’ 82 β†’ 103 β†’ 143 β†’ 158 β†’ 203 PassCount: 34 yes (1), 48 yes (2), 21 no, 40 yes (3), 15 no, 45 yes (4) Output: Total: 203, Number of passes: 4
DECLARE Marks : ARRAY[1:6] OF INTEGER
DECLARE Index : INTEGER
DECLARE Total : INTEGER
DECLARE PassCount : INTEGER
// Marks already holds 34, 48, 21, 40, 15, 45
Total ← 0
PassCount ← 0
FOR Index ← 1 TO 6
  Total ← Total + Marks[Index]
  IF Marks[Index] >= 25
    THEN
      PassCount ← PassCount + 1
  ENDIF
NEXT Index
OUTPUT "Total: ", Total
OUTPUT "Number of passes: ", PassCount
marks = [34, 48, 21, 40, 15, 45]
total = 0
pass_count = 0
for index in range(6):
    total = total + marks[index]
    if marks[index] >= 25:
        pass_count = pass_count + 1
print("Total:", total)
print("Number of passes:", pass_count)
!Watch out

Setting Total to 0 inside the loop is a classic error. The total is wiped each time round, so only the last value is kept.

Finding the maximum and minimum

To find the maximum (largest), keep a variable `Highest`. Compare each value with it. If the value is bigger, it becomes the new `Highest`.

The minimum (smallest) works the same way with `Lowest` and `<`.

Start `Highest` at the smallest possible value and `Lowest` at the largest possible value. Another safe way is to start both at the first item.

e.g.Example
Marks out of 50: 34, 48, 21, 40, 15, 45. Highest: 0 β†’ 34 β†’ 48 (21, 40, 15, 45 are not bigger than 48) Lowest: 50 β†’ 34 β†’ 21 β†’ 15 (40, 45 are not smaller than 15) Output: Highest: 48, Lowest: 15
DECLARE Marks : ARRAY[1:6] OF INTEGER
DECLARE Index : INTEGER
DECLARE Highest : INTEGER
DECLARE Lowest : INTEGER
// Marks already holds 34, 48, 21, 40, 15, 45 (each out of 50)
Highest ← 0
Lowest ← 50
FOR Index ← 1 TO 6
  IF Marks[Index] > Highest
    THEN
      Highest ← Marks[Index]
  ENDIF
  IF Marks[Index] < Lowest
    THEN
      Lowest ← Marks[Index]
  ENDIF
NEXT Index
OUTPUT "Highest: ", Highest
OUTPUT "Lowest: ", Lowest
marks = [34, 48, 21, 40, 15, 45]
highest = 0
lowest = 50
for index in range(6):
    if marks[index] > highest:
        highest = marks[index]
    if marks[index] < lowest:
        lowest = marks[index]
print("Highest:", highest)
print("Lowest:", lowest)
!Watch out

If Lowest starts at 0, no mark is ever smaller, so the answer is wrong. Start Lowest at the top of the range (50 here) or at the first value.

Finding the average

The average (mean) is the total divided by how many values there are.

First total the values in a loop. Then divide once, after the loop.

The answer is usually not a whole number, so store it as `REAL`. Use `ROUND` to give a set number of decimal places.

e.g.Example
Marks 34, 48, 21, 40, 15, 45. Total = 203. Count = 6. Average = 203 Γ· 6 = 33.8333… Rounded to 2 decimal places: 33.83
DECLARE Marks : ARRAY[1:6] OF INTEGER
DECLARE Index : INTEGER
DECLARE Total : INTEGER
DECLARE Average : REAL
// Marks already holds 34, 48, 21, 40, 15, 45
Total ← 0
FOR Index ← 1 TO 6
  Total ← Total + Marks[Index]
NEXT Index
Average ← Total / 6
OUTPUT "Average: ", ROUND(Average, 2)
marks = [34, 48, 21, 40, 15, 45]
total = 0
for index in range(6):
    total = total + marks[index]
average = total / 6
print("Average:", round(average, 2))
!Watch out

Do not divide inside the loop. Also check the count is not 0 before dividing, or the program crashes.

Linear search

A linear search looks for a value by checking each item in a list, one at a time, from the start.

It stops when it finds the item, or when it reaches the end of the list.

Try it: the toy's list holds numbers, not names. Type 22 (the last item), press Search for it, then Run all. Then try 99, which is not there. Count the comparisons each time.

e.g.Example
List: Lucia, Hugo, Martina, Pablo, Sofia, Leo. Target: Pablo. Position 1: Lucia β‰  Pablo Position 2: Hugo β‰  Pablo Position 3: Martina β‰  Pablo Position 4: Pablo = Pablo β†’ Found ← TRUE Output: Pablo found at position 4 (4 comparisons) Target Ana: all 6 compared, none match β†’ Ana is not in the list
DECLARE Names : ARRAY[1:6] OF STRING
DECLARE Target : STRING
DECLARE Index : INTEGER
DECLARE Found : BOOLEAN
// Names already holds Lucia, Hugo, Martina, Pablo, Sofia, Leo
OUTPUT "Which name do you want to find? "
INPUT Target
Found ← FALSE
Index ← 1
WHILE Found = FALSE AND Index <= 6 DO
  IF Names[Index] = Target
    THEN
      Found ← TRUE
    ELSE
      Index ← Index + 1
  ENDIF
ENDWHILE
IF Found = TRUE
  THEN
    OUTPUT Target, " found at position ", Index
  ELSE
    OUTPUT Target, " is not in the list"
ENDIF
names = ["Lucia", "Hugo", "Martina", "Pablo", "Sofia", "Leo"]
target = input("Which name do you want to find? ")
found = False
index = 0
while found == False and index < 6:
    if names[index] == target:
        found = True
    else:
        index = index + 1
if found == True:
    print(target, "found at position", index + 1)
else:
    print(target, "is not in the list")
?Why?

A linear search works on any list, sorted or not. The cost is time: a list of 1000 names can need 1000 comparisons.

!Watch out

The `Found` flag stops the loop early. Without it the search carries on to the end even after the name is found.

There is something to try for this step in the IGCSE library.

Bubble sort

A bubble sort puts a list in order. It compares each pair of neighbours. If they are in the wrong order, it swaps them.

One trip through the list is a pass. After each pass the largest unsorted value has 'bubbled' to the end.

It stops when a pass makes no swaps. The toy uses a longer list: 6, 2, 9, 4, 7, 1, 5. Press Reset, then Step one comparison at a time. Watch the 9 move to the end in pass 1.

PassList after the passComparisonsSwaps
Start7, 3, 9, 2, 5
13, 7, 2, 5, 943
23, 2, 5, 7, 932
32, 3, 5, 7, 921
42, 3, 5, 7, 910 β†’ stop
DECLARE Numbers : ARRAY[1:5] OF INTEGER
DECLARE Top : INTEGER
DECLARE Index : INTEGER
DECLARE Temp : INTEGER
DECLARE Swap : BOOLEAN
Numbers[1] ← 7
Numbers[2] ← 3
Numbers[3] ← 9
Numbers[4] ← 2
Numbers[5] ← 5
Top ← 5
REPEAT
  Swap ← FALSE
  FOR Index ← 1 TO Top - 1
    IF Numbers[Index] > Numbers[Index + 1]
      THEN
        Temp ← Numbers[Index]
        Numbers[Index] ← Numbers[Index + 1]
        Numbers[Index + 1] ← Temp
        Swap ← TRUE
    ENDIF
  NEXT Index
  Top ← Top - 1
UNTIL Swap = FALSE OR Top = 1
# Python has no REPEAT ... UNTIL, so a WHILE loop is used
numbers = [7, 3, 9, 2, 5]
top = 5
swap = True
while swap == True and top > 1:
    swap = False
    for index in range(top - 1):
        if numbers[index] > numbers[index + 1]:
            temp = numbers[index]
            numbers[index] = numbers[index + 1]
            numbers[index + 1] = temp
            swap = True
    top = top - 1
print(numbers)
?Why?

The swap uses `Temp` because `A ← B` then `B ← A` would lose A's value. `Temp` keeps a copy. `Top` goes down by 1 each pass because the end of the list is already sorted.

!Watch out

The loop runs to `Top - 1`, not `Top`. The last comparison is item `Top - 1` with item `Top`. Going to `Top` would compare with an item past the end.

There is something to try for this step in the IGCSE library.

How many comparisons? (extra)

This formula is not in the syllabus. It is useful for understanding why bubble sort is slow for long lists.

In the worst case every pass is needed. With n items, pass 1 makes n βˆ’ 1 comparisons, pass 2 makes n βˆ’ 2, and so on down to 1.

Adding these gives n(n βˆ’ 1) Γ· 2. Double the list and the work goes up about four times.

e.g.Example
n = 5: (5 Γ— 4) Γ· 2 = 10 comparisons. Check: 4 + 3 + 2 + 1 = 10. This matches the table above. n = 30 (a class): (30 Γ— 29) Γ· 2 = 435 n = 1000: (1000 Γ— 999) Γ· 2 = 499 500
?Why?

This shows why a sort that is fine for a class list is too slow for a million customers. Faster sorts exist, but they are not in this course.

7.5 Validation and verification

Validation and verification are different

Validation is an automatic check by the program that data is reasonable and follows set rules. It happens on input.

Verification checks that data has been copied or typed correctly from the source. It does not check if the data is sensible.

You need both. Validation accepts an age of 15 even if the student is 16. Verification catches that typing mistake.

ValidationVerification
ChecksIs the data sensible and allowed?Is the data the same as the original?
Who does itThe program, automaticallyThe user or a second entry
ExampleAge must be 11 to 18Type the password twice
!Watch out

Validation cannot prove data is correct. It only proves the data is possible. 15 is a possible age, even if the real age is 16.

Range, length, type and presence checks

Range check: a number must be between a lowest and a highest value. Example: an exam mark from 0 to 100.

Length check: the number of characters must be exact or within limits. Example: a password of 8 to 20 characters.

Type check: data must be the right data type. Example: an age must be a whole number, not 'ten'. Presence check: something must be entered. The field cannot be left blank.

DECLARE UserName : STRING
DECLARE Password : STRING
// presence check: something must be typed
OUTPUT "Enter your name: "
INPUT UserName
WHILE UserName = "" DO
  OUTPUT "Error: the name cannot be blank. Enter your name: "
  INPUT UserName
ENDWHILE
// length check: 8 to 20 characters
OUTPUT "Choose a password (8 to 20 characters): "
INPUT Password
WHILE LENGTH(Password) < 8 OR LENGTH(Password) > 20 DO
  OUTPUT "Error: use 8 to 20 characters. Try again: "
  INPUT Password
ENDWHILE
user_name = input("Enter your name: ")
# presence check: something must be typed
while user_name == "":
    user_name = input("Error: the name cannot be blank. Enter your name: ")
# length check: 8 to 20 characters
password = input("Choose a password (8 to 20 characters): ")
while len(password) < 8 or len(password) > 20:
    password = input("Error: use 8 to 20 characters. Try again: ")
print("Welcome,", user_name)
?Why?

Each check stops a different kind of bad data. A range check alone accepts a blank box in some systems. A presence check alone accepts '9999' as an age.

Format and check digit checks

Format check: data must follow a set pattern of characters. Example: a Spanish DNI is 8 digits then 1 letter, like `12345678Z`.

Check digit: an extra digit worked out from the other digits and added to the end. The computer recalculates it. If the answers differ, there is an entry error.

Try the toy: type in each box of the form and watch its checks pass or fail. Press Fill with valid data, then change the Student ID to break the format check. Type 9780140449136 in the ISBN box, then change one digit to break the check digit.

e.g.Example
ISBN-13 check digit for 978014044913? Weights alternate 1, 3, 1, 3 … 9Γ—1 + 7Γ—3 + 8Γ—1 + 0Γ—3 + 1Γ—1 + 4Γ—3 + 0Γ—1 + 4Γ—3 + 4Γ—1 + 9Γ—3 + 1Γ—1 + 3Γ—3 = 9 + 21 + 8 + 0 + 1 + 12 + 0 + 12 + 4 + 27 + 1 + 9 = 104 104 MOD 10 = 4. Check digit = 10 βˆ’ 4 = 6. Full ISBN: 9780140449136 Extra (Spain): the DNI letter is a check character too. 12345678 MOD 23 = 14, and position 14 in the official letter list is Z.
?Why?

A check digit catches common typing errors, like one wrong digit or two digits swapped. A format check would not, because the pattern still looks right.

!Watch out

A format check is about the pattern (digits here, letter there). A check digit is about a calculation. Do not mix them up.

There is something to try for this step in the IGCSE library.

Writing a validation loop

A validation loop asks for input, checks it, and asks again until it passes.

Use `REPEAT … UNTIL` or `WHILE … ENDWHILE`. Always give an error message that says what is allowed.

Python can also do a type check. `isdigit()` returns True only if every character is a digit.

e.g.Example
Inputs typed: (blank), ten, 25, 15 (blank) β†’ presence fails β†’ error ten β†’ type fails β†’ error 25 β†’ range fails (more than 18) β†’ error 15 β†’ passes β†’ Age accepted: 15
DECLARE Age : INTEGER
REPEAT
  OUTPUT "Enter your age (11 to 18): "
  INPUT Age
  IF Age < 11 OR Age > 18
    THEN
      OUTPUT "Error: age must be from 11 to 18"
  ENDIF
UNTIL Age >= 11 AND Age <= 18
OUTPUT "Age accepted: ", Age
entry = input("Enter your age (11 to 18): ")
# presence, type and range checks, repeated until the entry passes
while entry == "" or not entry.isdigit() or int(entry) < 11 or int(entry) > 18:
    print("Error: enter a whole number from 11 to 18")
    entry = input("Enter your age (11 to 18): ")
age = int(entry)
print("Age accepted:", age)
!Watch out

The condition in UNTIL is the opposite of the error condition. Error if Age < 11 OR Age > 18. Stop when Age >= 11 AND Age <= 18.

Verification: visual check and double entry

A visual check means the user reads the data on screen and compares it with the original document. Example: checking a delivery address before you pay.

A double entry check means the data is typed twice. The computer compares the two entries. If they differ, it asks again.

Double entry is used for new passwords and email addresses, where you cannot see a mistake.

DECLARE Password : STRING
DECLARE Confirm : STRING
REPEAT
  OUTPUT "Enter a new password: "
  INPUT Password
  OUTPUT "Enter the same password again: "
  INPUT Confirm
  IF Password <> Confirm
    THEN
      OUTPUT "The two entries do not match. Start again."
  ENDIF
UNTIL Password = Confirm
OUTPUT "Password saved"
password = input("Enter a new password: ")
confirm = input("Enter the same password again: ")
while password != confirm:
    print("The two entries do not match. Start again.")
    password = input("Enter a new password: ")
    confirm = input("Enter the same password again: ")
print("Password saved")
!Watch out

Double entry does not prove the data is correct. If you type the same wrong password twice, both entries match and it is accepted.

8.1 Programming concepts

Variables: named boxes in memory

A variable is a named place in memory that holds one value. The value can change while the program runs.

The name is called an identifier. Cambridge pseudocode writes identifiers in PascalCase, such as `TotalScore`.

`DECLARE` gives the name and the data type before the variable is used.

The arrow `←` means assignment: work out the value on the right, then store it in the variable on the left.

e.g.Example
Score ← 0 stores 0. Score ← Score + 5 reads the old value (0), adds 5, and stores 5. Output: 5
DECLARE Score : INTEGER
Score ← 0
Score ← Score + 5
OUTPUT Score
score = 0
score = score + 5
print(score)
?Why?

Python does not need a declaration. It works out the type from the value. Cambridge pseudocode declares every variable so the type is clear to the reader.

!Watch out

Read `Score ← Score + 5` from right to left. It is not a maths equation. It means: work out Score + 5, then put the answer back in Score.

Constants: values that never change

A constant is a named value that is set once. It cannot change while the program runs.

In pseudocode write `CONSTANT`, the name, `←` and the value. No data type is given: the value shows it.

Python has no true constants. Programmers write the name in CAPITALS to mean: do not change this.

e.g.Example
Spain's standard VAT (IVA) is 21%, so the rate is 0.21. 50.0 Γ— (1 + 0.21) = 50.0 Γ— 1.21 = 60.5 Output: Price with VAT: 60.5
CONSTANT VATRate ← 0.21
DECLARE Price : REAL
Price ← 50.0
OUTPUT "Price with VAT: ", Price * (1 + VATRate)
VAT_RATE = 0.21
price = 50.0
print("Price with VAT:", price * (1 + VAT_RATE))
?Why?

If the VAT rate changes, you edit one line instead of every line that uses 0.21. The name also tells the reader what 0.21 means. The program cannot change it by mistake.

!Watch out

Use a constant only for a value that is fixed for the whole run, like a tax rate or the size of an array. A value that the program updates, like a score, must be a variable.

The five data types

A data type says what kind of value a variable holds. It decides how the value is stored and what you can do with it.

Cambridge uses five data types. A CHAR goes in single quotes. A STRING goes in double quotes.

Data typeWhat it holdsExamplesPython
INTEGERA whole number: positive, negative or zero25, -3, 0int
REALA number with a decimal part3.142, -0.5, 19.99float
CHARExactly one character'A', '7', '?'str of length 1 (no separate char type)
STRINGZero or more characters (text)"Madrid", "28013", ""str
BOOLEANOnly TRUE or FALSETRUE, FALSETrue, False
DECLARE Age : INTEGER
DECLARE Height : REAL
DECLARE Grade : CHAR
DECLARE FirstName : STRING
DECLARE IsMember : BOOLEAN
Age ← 15
Height ← 1.68
Grade ← 'A'
FirstName ← "Lucia"
IsMember ← TRUE
age = 15
height = 1.68
grade = "A"
first_name = "Lucia"
is_member = True
print(type(age), type(height), type(grade), type(first_name), type(is_member))
?Why?

The type decides which operations make sense. You can add two INTEGERs. You cannot multiply two names.

Choosing the right data type

Ask two questions. Will I do maths with it? Can it have a decimal part?

A phone number or a postcode is a STRING. You never add phone numbers, and an INTEGER would lose a leading 0.

Use BOOLEAN for anything with only two states: paid or not paid, logged in or not.

DataBest typeReason
Number of pupils in a class: 28INTEGERCounted, so always whole
Price of a metro ticket: 1.50REALHas a decimal part
Shoe size: 38.5REALCan have a half
Exam grade: 'B'CHARAlways one character
Barcelona postcode: "08001"STRINGStarts with 0 and no maths is done on it
Has the pupil paid for the trip?BOOLEANOnly two answers
!Watch out

Do not store a phone number as INTEGER. 0034 912 345 678 would lose its zeros and spaces. Exam answers need a reason: STRING, because it can start with 0 and no calculations are done on it.

Input and output

Input is data going into the program, usually typed by the user. `INPUT Name` waits and stores what is typed in Name.

Output is data coming out, usually on screen. `OUTPUT` can show text and variables separated by commas.

Always show a message before an INPUT so the user knows what to type. This message is a prompt.

In Python, `input()` always gives a string. Wrap it in `int()` or `float()` to get a number.

e.g.Example
The user types Lucia, then 15. Age + 1 = 15 + 1 = 16 Output: Hello Lucia, next year you will be 16
DECLARE Name : STRING
DECLARE Age : INTEGER
OUTPUT "Enter your name"
INPUT Name
OUTPUT "Enter your age"
INPUT Age
OUTPUT "Hello ", Name, ", next year you will be ", Age + 1
name = input("Enter your name: ")
age = int(input("Enter your age: "))
print("Hello " + name + ", next year you will be", age + 1)
!Watch out

Forgetting `int()` is the most common Python bug here. Without it, age holds the text "15". Then `age + 1` crashes, because Python cannot add text and a number.

Sequence: one step after another

Sequence means the statements run one after another, in the order they are written.

The order matters. If a value is used before it is worked out, the answer is wrong.

e.g.Example
Three cinema tickets at 7.50 euros each. Total = 3 Γ— 7.50 = 22.50 Output: Total to pay: 22.5
CONSTANT TicketPrice ← 7.50
DECLARE Tickets : INTEGER
DECLARE Total : REAL
Tickets ← 3
Total ← Tickets * TicketPrice
OUTPUT "Total to pay: ", Total
TICKET_PRICE = 7.50
tickets = 3
total = tickets * TICKET_PRICE
print("Total to pay:", total)
!Watch out

Swap the last two lines and the program outputs Total before it has a value. Exam questions often ask you to find statements in the wrong order.

Selection with IF

Selection means the program chooses which statements to run, using a condition.

A condition is a question with a TRUE or FALSE answer, such as `Mark >= 50`.

The THEN part runs when the condition is TRUE. The ELSE part runs when it is FALSE. ELSE is optional. ENDIF closes the IF.

e.g.Example
Mark = 62. Is 62 >= 50? TRUE, so the output is Pass. Mark = 49. Is 49 >= 50? FALSE, so the output is Fail.
DECLARE Mark : INTEGER
OUTPUT "Enter the mark"
INPUT Mark
IF Mark >= 50
  THEN
    OUTPUT "Pass"
  ELSE
    OUTPUT "Fail"
ENDIF
mark = int(input("Enter the mark: "))
if mark >= 50:
    print("Pass")
else:
    print("Fail")
?Why?

Python uses a colon and indentation to show which lines belong to the IF. Pseudocode uses THEN, ELSE and ENDIF. Both mean the same thing.

Nested IF: up to three levels

A nested IF is an IF inside another IF. Use it when the next question depends on the first answer.

Cambridge questions use nesting up to three levels deep. More than that is hard to read.

In pseudocode the next IF goes inside the ELSE. Python can shorten ELSE followed by IF to `elif`.

e.g.Example
Mark = 58. Level 1: is 58 >= 70? FALSE, so go to ELSE. Level 2: is 58 >= 55? TRUE, so output Grade B. Level 3 is never checked. Output: Grade B
DECLARE Mark : INTEGER
Mark ← 58
IF Mark >= 70
  THEN
    OUTPUT "Grade A"
  ELSE
    IF Mark >= 55
      THEN
        OUTPUT "Grade B"
      ELSE
        IF Mark >= 40
          THEN
            OUTPUT "Grade C"
          ELSE
            OUTPUT "Ungraded"
        ENDIF
    ENDIF
ENDIF
mark = 58
if mark >= 70:
    print("Grade A")
elif mark >= 55:
    print("Grade B")
elif mark >= 40:
    print("Grade C")
else:
    print("Ungraded")
!Watch out

Test the highest boundary first. If you test `Mark >= 40` first, a mark of 85 outputs Grade C and stops. Every IF needs its own ENDIF.

Selection with CASE

`CASE OF` picks one path from many, using the value of one variable.

Each line has a value, a colon, then the statement to run. `OTHERWISE` catches every value that is not listed. ENDCASE closes it.

CASE is neater than a long chain of IFs when one variable is compared with fixed values.

Python 3.10 and later has `match` and `case`. `case _` works like OTHERWISE. Older Python uses if, elif and else.

e.g.Example
The user types S. 'A'? No. 'S'? Yes, so output Subtracting and leave the CASE. The user types X: no value matches, so OTHERWISE outputs Unknown choice.
DECLARE Choice : CHAR
OUTPUT "A = add, S = subtract, Q = quit"
INPUT Choice
CASE OF Choice
  'A' : OUTPUT "Adding"
  'S' : OUTPUT "Subtracting"
  'Q' : OUTPUT "Goodbye"
  OTHERWISE : OUTPUT "Unknown choice"
ENDCASE
choice = input("A = add, S = subtract, Q = quit: ")
match choice:
    case "A":
        print("Adding")
    case "S":
        print("Subtracting")
    case "Q":
        print("Goodbye")
    case _:
        print("Unknown choice")
!Watch out

CASE compares one variable with exact values. For a condition that joins two tests, like `Mark >= 50 AND Attendance > 90`, use IF.

Relational and logical operators

Relational operators compare two values. The answer is always TRUE or FALSE.

Logical operators join conditions. `AND` needs both to be TRUE. `OR` needs at least one TRUE. `NOT` swaps TRUE and FALSE.

The table uses Age = 15.

PseudocodePythonMeaningExampleResult
===equal toAge = 15TRUE
<>!=not equal toAge <> 15FALSE
<<less thanAge < 12FALSE
<=<=less than or equal toAge <= 15TRUE
>>greater thanAge > 12TRUE
>=>=greater than or equal toAge >= 16FALSE
ANDandboth are TRUEAge > 11 AND Age < 19TRUE
ORorat least one is TRUEAge < 5 OR Age > 65FALSE
NOTnotthe oppositeNOT (Age = 15)FALSE
e.g.Example
A metro discount is for under-26s or over-65s who have a travel card. Age < 26 β†’ TRUE. Age >= 65 β†’ FALSE. TRUE OR FALSE β†’ TRUE. HasCard = TRUE β†’ TRUE. TRUE AND TRUE β†’ TRUE. Output: Discount fare
DECLARE Age : INTEGER
DECLARE HasCard : BOOLEAN
Age ← 15
HasCard ← TRUE
IF (Age < 26 OR Age >= 65) AND HasCard = TRUE
  THEN
    OUTPUT "Discount fare"
  ELSE
    OUTPUT "Full fare"
ENDIF
age = 15
has_card = True
if (age < 26 or age >= 65) and has_card == True:
    print("Discount fare")
else:
    print("Full fare")
!Watch out

In pseudocode `=` compares. In Python `=` stores a value and `==` compares. `if age = 15:` is a syntax error in Python. Use brackets when you mix AND and OR, so the order is clear.

Arithmetic operators, DIV and MOD

Cambridge uses seven arithmetic operators: `+`, `-`, `*`, `/`, `^`, `DIV` and `MOD`.

`/` gives the full answer, which can have a decimal part. `DIV` gives only the whole-number part. `MOD` gives the remainder.

You may see `DIV(17, 5)` or `17 DIV 5`. Both mean the same.

Try the toy: share 17 sweets between 5 friends. Each friend gets DIV(17, 5) = 3. MOD(17, 5) = 2 sweets are left over.

PseudocodePythonMeaningExampleResult
++add7 + 29
--subtract7 - 25
**multiply7 * 214
//divide7 / 23.5
^**raise to the power2 ^ 38
DIV//whole-number divisionDIV(7, 2)3
MOD%remainder after divisionMOD(7, 2)1
e.g.Example
A film lasts 135 minutes. DIV(135, 60) = 2, because 60 fits into 135 two whole times. MOD(135, 60) = 135 - (2 Γ— 60) = 135 - 120 = 15 Output: 2 hours and 15 minutes
DECLARE Minutes : INTEGER
Minutes ← 135
OUTPUT DIV(Minutes, 60), " hours and ", MOD(Minutes, 60), " minutes"
minutes = 135
print(minutes // 60, "hours and", minutes % 60, "minutes")
?Why?

MOD is very useful. `MOD(Number, 2) = 0` is TRUE when a number is even. `MOD(Number, 10)` gives the last digit, as used in check digits.

!Watch out

In Python `^` does something else, so use `**` for power. Also, Python `/` always gives a REAL: `6 / 2` gives `3.0`.

There is something to try for this step in the IGCSE library.

Iteration: the count-controlled FOR loop

Iteration means repeating a block of statements. A block that repeats is called a loop.

A count-controlled loop repeats a set number of times. Cambridge writes it `FOR … TO … NEXT`.

The loop variable counts for you. `STEP` sets how much it goes up, or down, each time.

e.g.Example
First loop: Count takes 1, 2, 3, 4, 5. Output: 3, 6, 9, 12, 15 (one per line) Second loop: Count goes 10, 8, 6, 4, 2, 0.
DECLARE Count : INTEGER
FOR Count ← 1 TO 5
  OUTPUT Count * 3
NEXT Count
FOR Count ← 10 TO 0 STEP -2
  OUTPUT Count
NEXT Count
for count in range(1, 6):
    print(count * 3)
for count in range(10, -1, -2):
    print(count)
!Watch out

Python `range` stops before its end value. `FOR Count ← 1 TO 5` becomes `range(1, 6)`. Counting down to 0 becomes `range(10, -1, -2)`.

The pre-condition WHILE loop

A pre-condition loop checks its condition before each pass. Cambridge writes it `WHILE … DO … ENDWHILE`.

It repeats while the condition is TRUE. If the condition is FALSE at the start, the loop body never runs.

Use it when you do not know how many passes are needed.

e.g.Example
1000 euros grows by 10% a year. When does it reach 1500? 1000 < 1500 TRUE β†’ 1100, Years 1 1100 < 1500 TRUE β†’ 1210, Years 2 1210 < 1500 TRUE β†’ 1331, Years 3 1331 < 1500 TRUE β†’ 1464.10, Years 4 1464.10 < 1500 TRUE β†’ 1610.51, Years 5 1610.51 < 1500 FALSE β†’ stop Output: Years needed: 5
DECLARE Savings : REAL
DECLARE Years : INTEGER
Savings ← 1000.0
Years ← 0
WHILE Savings < 1500 DO
  Savings ← Savings * 1.1
  Years ← Years + 1
ENDWHILE
OUTPUT "Years needed: ", Years
savings = 1000.0
years = 0
while savings < 1500:
    savings = savings * 1.1
    years = years + 1
print("Years needed:", years)
!Watch out

Something inside the loop must change the condition. If Savings never changed, the loop would never stop. This is an infinite loop.

The post-condition REPEAT UNTIL loop

A post-condition loop checks its condition after each pass. Cambridge writes it `REPEAT … UNTIL`.

The body always runs at least once. The loop stops when the condition becomes TRUE.

It suits validation: ask for the input, and ask again until it is acceptable.

Python has no REPEAT UNTIL. Use `while True:` and `break` out of the loop when the stop condition is TRUE.

e.g.Example
The user types 150: condition FALSE, so repeat. The user types -3: FALSE, so repeat. The user types 72: TRUE, so stop. Output: Accepted: 72
DECLARE Mark : INTEGER
REPEAT
  OUTPUT "Enter a mark from 0 to 100"
  INPUT Mark
UNTIL Mark >= 0 AND Mark <= 100
OUTPUT "Accepted: ", Mark
while True:
    mark = int(input("Enter a mark from 0 to 100: "))
    if mark >= 0 and mark <= 100:
        break
print("Accepted:", mark)
!Watch out

WHILE keeps going while its condition is TRUE. UNTIL stops when its condition is TRUE. So the conditions are opposites. `WHILE Mark < 0 OR Mark > 100` matches `UNTIL Mark >= 0 AND Mark <= 100`.

Choosing the right loop

First ask: do I know how many times? If yes, use FOR.

If not, ask: must it run at least once? If yes, use REPEAT UNTIL. If it might need to run zero times, use WHILE.

LoopCondition checkedRuns at least once?Use it whenExample
FOR (count-controlled)No condition: it countsRuns a fixed number of timesYou know how many timesEnter 30 marks
WHILE (pre-condition)Before each passNo, it can run 0 timesUnknown number of times, maybe noneAdd sales until the target is reached
REPEAT UNTIL (post-condition)After each passYes, alwaysUnknown number, but at least oneAsk for a PIN until it is correct
!Watch out

If a question says 'use a pre-condition loop', you must use WHILE. Using a different loop loses marks, even if the program works.

Totalling and counting

Totalling keeps a running total: `Total ← Total + Value`.

Counting adds 1 each time something happens: `Count ← Count + 1`.

Set both to 0 before the loop starts.

e.g.Example
Marks typed: 45, 72, 60, 38, 90 Total = 45 + 72 + 60 + 38 + 90 = 305 Average = 305 / 5 = 61 (Python shows 61.0) Passes (50 or more): 72, 60, 90, so PassCount = 3
DECLARE Total : INTEGER
DECLARE PassCount : INTEGER
DECLARE Mark : INTEGER
DECLARE Count : INTEGER
Total ← 0
PassCount ← 0
FOR Count ← 1 TO 5
  OUTPUT "Enter mark"
  INPUT Mark
  Total ← Total + Mark
  IF Mark >= 50
    THEN
      PassCount ← PassCount + 1
  ENDIF
NEXT Count
OUTPUT "Total: ", Total
OUTPUT "Average: ", Total / 5
OUTPUT "Passes: ", PassCount
total = 0
pass_count = 0
for count in range(1, 6):
    mark = int(input("Enter mark: "))
    total = total + mark
    if mark >= 50:
        pass_count = pass_count + 1
print("Total:", total)
print("Average:", total / 5)
print("Passes:", pass_count)
!Watch out

If `Total ← 0` is inside the loop, it resets on every pass. The final total is then only the last mark.

String handling: LENGTH, UCASE, LCASE

A string is a sequence of characters. Each character has a position number.

`LENGTH(s)` gives the number of characters, and spaces count.

`UCASE(s)` gives the string in capitals. `LCASE(s)` gives it in lower case. The variable itself only changes if you assign the result back.

PseudocodePythonWhat it gives
LENGTH(s)len(s)number of characters
UCASE(s)s.upper()the string in capitals
LCASE(s)s.lower()the string in lower case
SUBSTRING(s, start, length)s[start-1 : start-1+length]part of the string
e.g.Example
"Madrid Spain" has 6 letters + 1 space + 5 letters = 12 characters. Output: 12 MADRID SPAIN madrid spain
DECLARE City : STRING
City ← "Madrid Spain"
OUTPUT LENGTH(City)
OUTPUT UCASE(City)
OUTPUT LCASE(City)
city = "Madrid Spain"
print(len(city))
print(city.upper())
print(city.lower())
?Why?

UCASE and LCASE make comparisons fair. A user may type yes, YES or Yes. `IF UCASE(Answer) = "YES"` accepts all three.

SUBSTRING and the first position: 1 or 0

`SUBSTRING(s, start, length)` takes a piece out of a string. You give the start position and how many characters you want.

In Cambridge pseudocode the first character is position 1. In Python the first character is position 0.

Python uses slicing: `s[start:end]` takes characters from start up to, but not including, end.

Try the toy: type MADRID as the string. Set the start to 4 and the length to 3, and check you get RID. Then try LENGTH, UCASE and LCASE on the same string.

CharacterMADRID
Pseudocode position123456
Python index012345
e.g.Example
SUBSTRING("MADRID", 4, 3): start at position 4 (R) and take 3 characters β†’ RID. Python: start = 4 - 1 = 3. End = 3 + 3 = 6. word[3:6] β†’ "RID". Rule: Python start = pseudocode start - 1. Python end = Python start + length.
DECLARE Word : STRING
Word ← "MADRID"
OUTPUT SUBSTRING(Word, 1, 3)
OUTPUT SUBSTRING(Word, 4, 3)
OUTPUT SUBSTRING(Word, 1, 1)
word = "MADRID"
print(word[0:3])
print(word[3:6])
print(word[0])
!Watch out

The exam says whether the first character is 0 or 1, so read the question. The third value in SUBSTRING is a length, not an end position.

There is something to try for this step in the IGCSE library.

Procedures and parameters

A subroutine is a named block of code that does one job. You write it once and call it whenever you need it.

A procedure is a subroutine that does a task but does not give a value back. You run it with `CALL`.

A parameter is a variable in the heading that receives a value when the subroutine is called. Cambridge uses up to three.

The value you pass in is called the argument. Here "Ana" and 2 are the arguments.

e.g.Example
CALL Greet("Ana", 2) puts "Ana" in Name and 2 in Times. The loop runs 2 times. Output: Hello Ana Hello Ana
PROCEDURE Greet(Name : STRING, Times : INTEGER)
  DECLARE Count : INTEGER
  FOR Count ← 1 TO Times
    OUTPUT "Hello ", Name
  NEXT Count
ENDPROCEDURE

CALL Greet("Ana", 2)
def greet(name, times):
    for count in range(1, times + 1):
        print("Hello", name)

greet("Ana", 2)
?Why?

Subroutines stop you repeating code. The program is shorter, each part can be tested on its own, and a fix is made in one place.

Functions: subroutines that return a value

A function is a subroutine that returns a value to the place where it was called.

The heading gives the type of the value it returns, such as `RETURNS REAL`. `RETURN` sends the value back.

A function gives a value, so you use it inside an expression or an OUTPUT. You do not use CALL.

ProcedureFunction
Returns a value?NoYes, with RETURN
PseudocodePROCEDURE … ENDPROCEDUREFUNCTION … RETURNS type … ENDFUNCTION
How it is usedCALL Name(…) on its own lineInside an expression: X ← Name(…)
Pythondef, with no return valuedef, with return
e.g.Example
Average(70, 64, 85) (70 + 64 + 85) / 3 = 219 / 3 = 73 The function returns 73, which is stored in Result. Output: Average: 73 (Python shows 73.0)
FUNCTION Average(Mark1 : REAL, Mark2 : REAL, Mark3 : REAL) RETURNS REAL
  RETURN (Mark1 + Mark2 + Mark3) / 3
ENDFUNCTION

DECLARE Result : REAL
Result ← Average(70, 64, 85)
OUTPUT "Average: ", Result
def average(mark1, mark2, mark3):
    return (mark1 + mark2 + mark3) / 3

result = average(70, 64, 85)
print("Average:", result)
!Watch out

A function must RETURN a value of the type in its heading. Writing CALL before a function, or RETURN inside a procedure, loses marks.

Local and global variables

Scope means the part of the program where a variable can be used.

A global variable is declared in the main program. Every part of the program can use it.

A local variable is declared inside a subroutine. It exists only while that subroutine runs, and nothing outside can see it.

In Python a function can read a global variable. To change it, the function must first say `global`.

e.g.Example
Bonus = 5, so Doubled = 5 Γ— 2 = 10. Score = 0 + 10 = 10. Output: 10 OUTPUT Doubled in the main program would be an error: Doubled no longer exists.
DECLARE Score : INTEGER        // global
Score ← 0

PROCEDURE AddBonus(Bonus : INTEGER)
  DECLARE Doubled : INTEGER    // local
  Doubled ← Bonus * 2
  Score ← Score + Doubled
ENDPROCEDURE

CALL AddBonus(5)
OUTPUT Score
score = 0  # global

def add_bonus(bonus):
    global score
    doubled = bonus * 2  # local
    score = score + doubled

add_bonus(5)
print(score)
?Why?

Local variables keep each subroutine separate. Two subroutines can both use a variable called Count without clashing. The memory is freed when the subroutine ends.

!Watch out

Too many global variables make bugs hard to find, because any line could change them. Pass values in as parameters and return results instead.

Library routines: MOD, DIV, ROUND, RANDOM

A library routine is a ready-made, tested subroutine that comes with the language. You use it without writing it.

Cambridge expects four: `MOD`, `DIV`, `ROUND` and `RANDOM`. MOD and DIV were in the arithmetic step.

`ROUND(Value, Places)` rounds to a number of decimal places. `ROUND(6.97354, 2)` gives 6.97.

`RANDOM()` gives a random REAL number from 0 to 1. Multiply it and round it to get the range you want.

e.g.Example
ROUND(19.987, 2) = 19.99 Say RANDOM() gives 0.62. 0.62 Γ— 5 = 3.1. ROUND(3.1, 0) = 3. 3 + 1 = 4. Output: You rolled 4 RANDOM() Γ— 5 is from 0 to 5, so Dice is always from 1 to 6.
DECLARE Price : REAL
DECLARE Dice : INTEGER
Price ← 19.987
OUTPUT ROUND(Price, 2)
Dice ← ROUND(RANDOM() * 5, 0) + 1
OUTPUT "You rolled ", Dice
import random
price = 19.987
print(round(price, 2))
dice = random.randint(1, 6)
print("You rolled", dice)
?Why?

Library routines save time. They are already tested, so they are reliable.

!Watch out

Python's `round()` sends an exact half to the nearest even number: `round(2.5)` gives 2 and `round(3.5)` gives 4. Cambridge pseudocode questions expect normal rounding, where 2.5 becomes 3.

Writing maintainable programs

A maintainable program is easy for another programmer, or you months later, to read, fix and update.

Use meaningful identifiers: `TotalPrice`, not `tp` or `x`.

Add comments that explain what a part does and why. Pseudocode comments start with `//`. Python comments start with `#`.

Use procedures and functions so each job lives in one named place. Use constants for fixed values, and indent code inside IFs, loops and subroutines.

e.g.Example
Hard to maintain: x ← y / 30 + 8.5 Maintainable (above): 600 / 30 = 20. 20 + 8.5 = 28.5 Output: Cost per pupil: 28.5
// Works out the cost of a school trip for one pupil
CONSTANT CoachCost ← 600.0
CONSTANT EntryFee ← 8.5

FUNCTION CostPerPupil(Pupils : INTEGER) RETURNS REAL
  // the coach is shared, the entry fee is paid by each pupil
  RETURN CoachCost / Pupils + EntryFee
ENDFUNCTION

OUTPUT "Cost per pupil: ", CostPerPupil(30)
# Works out the cost of a school trip for one pupil
COACH_COST = 600.0
ENTRY_FEE = 8.5

def cost_per_pupil(pupils):
    # the coach is shared, the entry fee is paid by each pupil
    return COACH_COST / pupils + ENTRY_FEE

print("Cost per pupil:", cost_per_pupil(30))
?Why?

Real programs are changed for years by many people. Clear code is cheaper to fix and less likely to get new bugs.

Study it

Open the study page and go one step at a time.

Check yourself

One question at a time. The reason shows after each answer.

Saved on this device.