Tutorial 0

❓️

Common Pseudocode Tasks & FAQs

9618 A-Level


There are many common tasks you will have to complete in exams - this page is essentially a repository of many of these key tasks you are expected to be able to perform for AS & A2 9618

If there is something else you'd like me to add to this page, please contact me

More details for specific aspects of pseudocode you have to know can be found on the tutorials pages

You should also ensure you are familiar with the built-in library modules provided in the pseudocode module insert too

1

Input into an Array

Often, we are required to ask the user to input data into an array

This is fairly simple to do - simply loop over all elements in the array and input at that particular index

In the example below, the user is asked to select their football team - both positions and players - this team sheet is then output to the user at the end

DECLARE position, player : ARRAY[1:11] OF STRING DECLARE n : INTEGER OUTPUT "Football Team Choice" FOR n <-- 1 TO 11 OUTPUT "Enter position for player ", n, " - e.g. GK:" INPUT position[n] OUTPUT "Enter name of player who will play in ", position[n] INPUT player[n] NEXT n OUTPUT "" OUTPUT "--- Team Sheet ---" OUTPUT "" FOR n <-- 1 TO 11 OUTPUT player[n], " (", position[n], ")" NEXT n

2

Find Min, Max, Sum & Average of Inputs

Although more common for IG 15 markers, some A-Level questions also require us to calculate the minimum, maximum, sum and mean of all numbers in an array

Looking at the code below, note:

  • We assign min & max as being the first element - this is since, e.g. if giving min a huge default value, then if all values happened to be larger than this, we would think the min was this default value we initially assigned it - same for max, if we chose a small/negative initial value, but the real values all happened to be less than it
  • We then loop through all elements in the array
  • Min: if the current element is smaller than the current minimum, then set this current element as the new minimum
  • Max: if the current element is bigger than the current maximum, then set this current element as the new maximum
  • Sum: add each element to the sum
  • Mean: divide the sum by the number of elements
  • Note: in this example, we could have also assigned sum to be the first element, then started our loop from element 2
DECLARE nums : ARRAY[1:5] OF INTEGER DECLARE min, max, sum, index : INTEGER DECLARE mean : REAL nums[1] <-- 6 nums[2] <-- 3 nums[3] <-- 8 nums[4] <-- 2 nums[5] <-- 5 sum <-- 0 min <-- nums[1] max <-- nums[1] FOR index <-- 1 TO 5 IF nums[index] < min THEN min <-- nums[index] ENDIF IF nums[index] > max THEN max <-- nums[index] ENDIF sum <-- sum + nums[index] NEXT index OUTPUT "Min: ", min OUTPUT "Max: ", max OUTPUT "Sum: ", sum OUTPUT "Mean: ", (sum / 5)

3

Find Min, Max, Sum & Average of Inputs

The code is effectively the same as that above, but here we should ideally assign the first input to be the min/max/sum - we could do this by having the first input before the loop, then only looping from 2 to n, or we could have a Boolean value (flag) representing whether we have got the first input yet

DECLARE min, max, sum, index, num, count : INTEGER DECLARE mean : REAL DECLARE isFirst : BOOLEAN isFirst <-- TRUE count <-- 0 sum <-- 0 REPEAT OUTPUT "Enter number or 0 to stop:" INPUT num IF num <> 0 THEN IF isFirst = TRUE OR num < min THEN min <-- num ENDIF IF isFirst = TRUE OR num > max THEN max <-- num ENDIF sum <-- sum + num count <-- count + 1 isFirst <-- FALSE ENDIF UNTIL num = 0 OUTPUT "Min: ", min OUTPUT "Max: ", max OUTPUT "Sum: ", sum OUTPUT "Count: ", count OUTPUT "Mean: ", (sum / count)

4

Linear Search

Often we want to search to see whether a specific value exists in an array - we can use linear search to do that:

  1. Initialise an index variable to point to first element in array
  2. Loop through array element-by-element
  3. If current element is the value we are searching for, then update flag/return TRUE if in a function etc
  4. If item hasn't been found after looping through all elements in the array, then we know the item doesn't exist
DECLARE nums : ARRAY[1:5] OF INTEGER DECLARE targetNumber, index : INTEGER DECLARE found : BOOLEAN nums[1] <-- 6 nums[2] <-- 3 nums[3] <-- 8 nums[4] <-- 2 nums[5] <-- 5 OUTPUT "Enter number to search for:" INPUT targetNumber index <-- 1 found <-- FALSE //loop to 5, since 5 elements in array WHILE found = FALSE AND index <= 5 DO IF nums[index] = targetNumber THEN found <-- TRUE ENDIF index <-- index + 1 ENDWHILE IF found = TRUE THEN OUTPUT targetNumber, " is in the array!" ELSE OUTPUT targetNumber, " is not in the array!" ENDIF

Note: we can also write this as a function, returning TRUE if we find the item, while returning FALSE if we have looped through the entire array and not found the item

DECLARE nums : ARRAY[1:5] OF INTEGER DECLARE targetNumber : INTEGER nums[1] <-- 6 nums[2] <-- 3 nums[3] <-- 8 nums[4] <-- 2 nums[5] <-- 5 OUTPUT "Enter number to search for:" INPUT targetNumber IF LinearSearch(nums, targetNumber) = TRUE THEN OUTPUT targetNumber, " is in the array!" ELSE OUTPUT targetNumber, " is not in the array!" ENDIF FUNCTION LinearSearch(arr : ARRAY OF INTEGER, toFind : INTEGER) RETURNS BOOLEAN DECLARE index : INTEGER FOR index <-- 1 TO 5 IF arr[index] = toFind THEN RETURN TRUE ENDIF NEXT index RETURN FALSE ENDFUNCTION

5

1D Bubble Sort

In order to sort an array in either ascending or descending order, we can use bubble sort:

  1. Assume array is unsorted and loop to the n-1 position (e.g. 1 to 4, if 5 elements) in the array on the first iteration
  2. Compare elements pair by pair - if in the wrong order (i.e. if current element bigger than next element for ascending order...or current element smaller than next for descending order), then swap them by making use of a temporary variable
  3. If elements are not sorted after looping over all pairs, then go back to the start

More detailed comments can be seen in the code

Note: when we swap, we need the temporary variable - this is since if we do a <-- b, both variables now have the same value at this point - b <-- a will be redundant - hence why we need to move one value into a temporary variable before we update it, then assign this temporary variable value to the other variable. Drawing a picture with arrows representing the assignments between 3 variables can help with understanding.

Note: to sort in descending order, simply change the > in the IF statement to a <

DECLARE arrayLength : INTEGER DECLARE nums : ARRAY[1:5] OF INTEGER DECLARE targetNumber : INTEGER arrayLength <-- 5 nums[1] <-- 6 nums[2] <-- 3 nums[3] <-- 8 nums[4] <-- 2 nums[5] <-- 5 //bubble sort DECLARE temp : INTEGER DECLARE isSorted : BOOLEAN DECLARE endIndex : INTEGER endIndex <-- arrayLength - 1 //if array has 5 elements, then loop to 4, since can't compare element 5 with element 6, as there is no element 6... isSorted <-- FALSE //assume unsorted by default WHILE isSorted = FALSE DO //loop while not sorted isSorted <-- TRUE //assume is sorted, at start of each pass over all pairs - if no swaps made, then loop will terminate after this iteration FOR index <-- 1 TO endIndex //compare current with next element - if wrong order, then swap IF nums[index] > nums[index + 1] THEN //swap, using temp variable as intermediate storage temp <-- nums[index] nums[index] <-- nums[index + 1] nums[index + 1] <-- temp //since at least 1 element is not in correct order, then we need to loop again to continue sorting/check isSorted <-- FALSE ENDIF NEXT index //rightmost element sorted on 1st loop, 2nd rightmost on 2nd loop etc - hence don't need to swap these already sorted elements endIndex <-- endIndex - 1 ENDWHILE //not bubble sort - just to output FOR index <-- 1 TO arrayLength OUTPUT nums[index] NEXT index

2D Bubble Sort

The logic for 2D bubble sort is the same as for 1D bubble sort - the only difference is that we need to swap each column individually inside the IF statement

DECLARE arr : ARRAY[1:5, 1:2] OF INTEGER DECLARE endIndex, tempCol1, tempCol2 : INTEGER DECLARE isSorted : BOOLEAN arr <-- [ [22, 100], [91, 82], [50, 67], [34, 15], [4, 58] ] OUTPUT "This program will sort the array in ascending order of column 1:" isSorted <-- FALSE //assume unsorted by default endIndex <-- 4 //if array has 5 elements, then loop to 4, since can't compare element 5 with element 6, as there is no element 6... WHILE isSorted = FALSE DO //loop while not sorted isSorted <-- TRUE //assume is sorted, at start of each pass over all pairs - if no swaps made, then loop will terminate after this iteration FOR n <-- 1 TO endIndex //compare current with next element - if wrong order, then swap IF arr[n, 1] > arr[n + 1, 1] THEN isSorted <-- FALSE //since at least 1 element is not in correct order, then we need to loop again to continue sorting/check //swap column 1 values tempCol1 <-- arr[n, 1] arr[n, 1] <-- arr[n + 1, 1] arr[n + 1, 1] <-- tempCol1 //swap column 2 values tempCol2 <-- arr[n, 2] arr[n, 2] <-- arr[n + 1, 2] arr[n + 1, 2] <-- tempCol2 ENDIF NEXT n //rightmost element sorted on 1st loop, 2nd rightmost on 2nd loop etc - hence don't need to swap these already sorted elements endIndex <-- endIndex - 1 ENDWHILE //output for testing: FOR n <-- 1 TO 5 OUTPUT arr[n, 1], " | ", arr[n, 2] NEXT n

6

Round to n Decimal Places

For IG/O-Level, we could use the ROUND function to round to a given number of decimal places, where the 2nd parameter is the number of decimal places to round to

Unfortunately, for A-Level we don't have that function and, as you can see, the approach below is a bit tricky, so it's unlikely they will ask it, however, I will include this example too, in case people are curious/want to round in any of their own code without using the IG function

Let's walkthrough an example Custom_ROUND(123.456, 2) to see how it works

  1. Since we are rounding to 2 decimal places, we first want to move the decimal place 2 places to the right - 10 ** decimalPlaces will cause tenPower to be assigned the value 100
  2. Multiplying 123.456 * 100 = 12345.6
  3. We then have to add 0.5, giving us 12346.1 as we have seen before to correctly round either up or down (since INT simply truncates the values after the decimal point - i.e. rounding down for positive numbers)
  4. INT(12346.1) will evaluate to 12346
  5. Finally, 12346 / 100 = 123.46
DECLARE numDecimalPlaces : INTEGER FOR numDecimalPlaces ← 0 TO 6 OUTPUT numDecimalPlaces, ": ", Custom_ROUND(123.456789, numDecimalPlaces) NEXT numDecimalPlaces FUNCTION Custom_ROUND(num : REAL, decimalPlaces : INTEGER) RETURNS REAL DECLARE tenPower : INTEGER tenPower ← 10 ** decimalPlaces RETURN INT((num * tenPower) + 0.5) / tenPower ENDFUNCTION

7

Force Round Up or Down

Since INT truncates positive numbers (effectively rounding down), then unlike IG where we needed to subtract 0.5 like ROUND(x - 0.5, 0) to round down (floor), in AS, we can simply use INT as is

If we want to always round up (ceiling), we can just +1 to the argument we are passing into the INT function

DECLARE n : REAL FOR n ← 1 TO 2 STEP 0.01 OUTPUT "--- ", n, " ---" OUTPUT "Round Down: ", INT(n) OUTPUT "Round Up: ", INT(n + 1) OUTPUT "" NEXT n

8

Generate Random Number in a Range

Returns a real number between 0-1 inclusive

Note: we can multiply and add an offset to generate a random number within a range as shown below

The offset should be the lower bound of our range - then we should multiply our random value by the range. For example, if we want a random number between -10 and 10, the smallest number in the range is -10 and the range is 20

If we want to make this an integer, we will have to use ROUND

OUTPUT RAND(1) //real number 0 to 1 exclusive OUTPUT INT(RAND(11)) //integer 0-10 OUTPUT 1 + INT(RAND(10)) //integer 1-10 OUTPUT -10 + RAND(20) //real number -10 to 10 exclusive OUTPUT 5 + INT(RAND(6)) //integer 5 to 10

9

For Loop - Custom & Negative Step

By default, when we create a for loop, there is an implied step of 1 - i.e. the for loop variable increments by 1 each iteration of the loop

We can change this via the STEP keyword as shown below:

DECLARE index : INTEGER OUTPUT "--- Odd Numbers ---" FOR index <-- 1 TO 100 STEP 2 OUTPUT index NEXT index OUTPUT "--- 5x Table Numbers ---" FOR index <-- 0 TO 100 STEP 5 OUTPUT index NEXT index OUTPUT "--- Countdown 10-0 Numbers ---" FOR index <-- 10 TO 0 STEP -1 OUTPUT index NEXT index

10

Loop Through Characters in a String

We can create a simple for loop from 1 to the length of the string, then get the current character with MID

DECLARE index : INTEGER DECLARE word : STRING word <-- "Supercalifragilisticexpialidocious" FOR index <-- 1 TO LENGTH(word) OUTPUT index, ": ", MID(word, index, 1) NEXT index

11

Check if Character is Uppercase, Lowercase, Digit or Other

To check if a character is in a given range, we can either:

  1. Use the ASC function to get the character's ASCII code, then check which range it falls within (e.g. 65 to 90 for uppercase etc...)
  2. Directly compare the character with other characters - e.g 'b' > 'a' AND 'b' <= 'z' will evaluate to TRUE, since 'b' has the ASCII code 98, 'a' is 97, 'z' is 122

Remember the following ASCII codes, since sometimes they're not given in exams:

  • '0' = 48
  • 'A' = 65
  • 'a' = 97

As shown below, since we have a finite integer range, we can use either CASE or nested IF statements to determine the character type - it's up to you

DECLARE c : CHAR DECLARE asciiVal : INTEGER c <-- 'A' asciiVal <-- ASC(c) CASE OF asciiVal 65 TO 90: OUTPUT c, " is uppercase" 97 TO 122: OUTPUT c, " is lowercase" 48 TO 57: OUTPUT c, " is a digit" OTHERWISE: OUTPUT c, " is a special character" ENDCASE c <-- 'b' IF c >= 'A' AND c <= 'Z' THEN OUTPUT c, " is uppercase" ELSE IF c >= 'a' AND c <= 'z' THEN OUTPUT c, " is lowercase" ELSE IF c >= '0' AND c <= '9' THEN OUTPUT c, " is a digit" ELSE OUTPUT c, " is a special character" ENDIF ENDIF ENDIF

12

Length Check

Suppose customers should enter their credit card number - for this simple example, let's assume all should be 16 digits

Note: for credit card/telephone numbers, we often want to store them as strings, not integers - this is so leading zeros aren't remove, so that additional characters can be allowed in telephone numbers such as +(123) 456-789 etc

DECLARE creditCardNumber : STRING OUTPUT "Please enter a FAKE credit card number of 16 digits:" INPUT creditCardNumber WHILE LENGTH(creditCardNumber) <> 16 DO OUTPUT "Not 16 digits - try again:" INPUT creditCardNumber ENDWHILE OUTPUT "Success!"

13

Range Check

Assume you have a pizza delivery business - customers should be able to order between 1 and 50 pizzas

DECLARE numPizzas : INTEGER OUTPUT "How many pizzas would you like to buy (1-50):" INPUT numPizzas WHILE numPizzas < 1 OR numPizzas > 50 DO OUTPUT numPizzas, " isn't in the range 1-50 - try again:" INPUT numPizzas ENDWHILE OUTPUT "Success!"

14

Format Check

The specific code for a format check will depend on the format you are checking - let's say for our example, a password has to:

  • Start with an uppercase letter
  • Be at least 8 characters
  • Contain at least 1 lowercase character, number and special character
  • Can't start and end with the same character
OUTPUT PasswordCheck("abcDef123#") OUTPUT PasswordCheck("AbcDef123#A") OUTPUT PasswordCheck("AbcDef123#") FUNCTION PasswordCheck(pw : STRING) RETURNS BOOLEAN DECLARE index : INTEGER DECLARE currentChar : CHAR DECLARE hasLowercase, hasNumber, hasSpecialChar : BOOLEAN IF LENGTH(pw) < 8 THEN RETURN FALSE ENDIF currentChar <-- MID(pw, 1, 1) IF currentChar < 'A' OR currentChar > 'Z' THEN RETURN FALSE ENDIF IF currentChar = MID(pw, LENGTH(pw), 1) THEN RETURN FALSE ENDIF FOR index <-- 2 TO LENGTH(pw) currentChar <-- MID(pw, index, 1) IF currentChar >= 'a' AND currentChar <= 'z' THEN hasLowercase <-- TRUE ELSE IF currentChar >= '0' AND currentChar <= '9' THEN hasNumber <-- TRUE ELSE IF currentChar < 'A' OR currentChar > 'Z' THEN hasSpecialChar <-- TRUE ENDIF ENDIF ENDIF NEXT index IF hasLowercase = TRUE AND hasNumber = TRUE AND hasSpecialChar = TRUE THEN RETURN TRUE ELSE RETURN FALSE ENDIF ENDFUNCTION

15

Odd or Even

We can simply MOD by 2 - if the answer is 0, the number is even, if the answer is 1, it's odd

This same logic can be used to check if a number is a multiple of anything - for example, if MOD(n, 5) = 0, then n would be a multiple of 5

DECLARE num : INTEGER num <-- 5 IF num MOD 2 = 0 THEN OUTPUT num, " is even" ELSE OUTPUT num, " is odd" ENDIF

16

Check if Number is Integer

There are at least 3 ways of checking if a number is an integer or not

DECLARE num : REAL num <-- 5.1 OUTPUT "--- Checking ", num, " ---" OUTPUT num = INT(num) OUTPUT num DIV 1 = num OUTPUT num MOD 1 = 0 num <-- 5 OUTPUT "--- Checking ", num, " ---" OUTPUT num = INT(num) OUTPUT num DIV 1 = num OUTPUT num MOD 1 = 0

17

Declare, Populate & Output 2D Array

Note: we usually loop through a 2D array with rows being the outer loop, the columns being the inner loop - this effectively loops left to right, top to bottom - the same way books in many languages like English are read

DECLARE grid : ARRAY[1:3, 1:3] OF INTEGER DECLARE row, col : INTEGER //looping through 2D array, populating/initialising array elements and outputting them FOR row <-- 1 TO 3 FOR col <-- 1 TO 3 grid[row, col] <-- INT(RAND(10)) OUTPUT row, ", ", col, ": ", grid[row, col] NEXT col NEXT row OUTPUT "" OUTPUT "---" OUTPUT "" DECLARE line : STRING //concatenating strings to output row on single line FOR row <-- 1 TO 3 line <-- "" FOR col <-- 1 TO 3 line <-- line & NUM_TO_STR(grid[row, col]) & ' ' NEXT col OUTPUT line NEXT row

18

Display Menu

To display a menu, we can simply output a list of options along with a "rogue value" to stop the program - we loop until that rogue value is entered

DECLARE menuChoice : INTEGER REPEAT OUTPUT "Please enter a choice: 1) Do something... 2) Do something else... 3) Exit" INPUT menuChoice CASE OF menuChoice 1: CALL A() 2: CALL B() ENDCASE UNTIL menuChoice = 3 OUTPUT "Goodbye..." PROCEDURE A() OUTPUT "Procedure A called..." ENDPROCEDURE PROCEDURE B() OUTPUT "Procedure B called..." ENDPROCEDURE

19

Procedures

A reminder - a procedure is a group of code we can use (call) via the CALL keyword - we can optionally have parameters which will have different arguments on different calls of the procedure, hence modifying the behaviour of that particular procedure call - like below, where we call the same procedure with 3 different syllabus codes

CALL DisplayPaperDetails("AS") CALL DisplayPaperDetails("A2") CALL DisplayPaperDetails("IB") PROCEDURE DisplayPaperDetails(syllabusName : STRING) DECLARE validSyllabus : BOOLEAN validSyllabus <-- FALSE syllabusName <-- TO_UPPER(syllabusName) IF syllabusName = "AS" THEN OUTPUT "--- Computer Science | AS | 9618 ---" OUTPUT "Paper 1: 1h 30m, Theory, 75 Marks" OUTPUT "Paper 2: 2h, Programming, 75 Marks" ELSE IF syllabusName = "A2" THEN OUTPUT "--- Computer Science | A2 | 9618 ---" OUTPUT "Paper 3: 1h 30m, Theory, 75 Marks" OUTPUT "Paper 4: 2h 30m, Programming, 75 Marks" ELSE OUTPUT syllabusName, " is an unknown syllabus" ENDIF ENDIF OUTPUT "" ENDPROCEDURE

20

Functions

Like procedures, functions are groups of code that can be used at different points throughout the program

There are two differences, however:

  1. Functions have to return a value
  2. We don't need the CALL keyword when calling a function - since we can use the function directly in another statement or expression like output, an if statement, calculation etc
DECLARE n : INTEGER FOR n <-- 1 TO 100 IF isSquareNumber(n) THEN OUTPUT "🟢 ", n, " is square" ELSE OUTPUT "🔴 ", n, " is not square" ENDIF NEXT n FUNCTION isSquareNumber(num : INTEGER) RETURNS BOOLEAN DECLARE temp : REAL temp <-- num ** 0.5 RETURN temp = ROUND(temp, 0) ENDFUNCTION

21

Reading from Files

The file Jokes.txt contains 36 lines. We continue looping while the end of file (EOF) hasn't been reached - note that READFILE will read the next line of the file

Note how we are first required to open the file in read mode and close it after use too - forgetting to close the file will lose you marks in the exam, so try not to forget! The site will give you a warning if you did forget too

DECLARE line : STRING OPENFILE Jokes.txt FOR READ WHILE NOT EOF(Jokes.txt) DO READFILE Jokes.txt, line OUTPUT line ENDWHILE CLOSEFILE Jokes.txt

22

Writing to Files

Suppose we want to keep writing names of students in your class to a file, until the user enters nothing

Note: opening a file in WRITE mode will overwrite (delete the old contents) of an existing file with that name, if one exists

DECLARE classmate : STRING OPENFILE Students.txt FOR WRITE REPEAT OUTPUT "Enter classmate's name:" INPUT classmate IF classmate <> "" THEN WRITEFILE Students.txt, classmate ENDIF UNTIL classmate = "" CLOSEFILE Students.txt

23

Appending to Files

Sometimes we might want to write to an existing file - to do that, we simply open the file in APPEND mode

Note how we still use WRITEFILE when appending - a keyword like APPENDFILE doesn't exist in the syllabus

Content will be written to the end of an existing file - see the next example for writing data at an arbitrary point in a file

DECLARE temperature : REAL DECLARE line : STRING OPENFILE Temperatures.txt FOR APPEND OUTPUT "Enter temperature for ", TODAY(), ": " INPUT temperature line <-- TODAY() & " " & NUM_TO_STR(temperature) WRITEFILE Temperatures.txt, line CLOSEFILE Temperatures.txt

24

Inserting to File at Arbitrary Position

While appending is useful, it is limited in that data is always written at the end - sometimes we might want to write at a different position though

Consider a list of programming languages stored alphabetically - when inserting, we would want to insert the new language at the correct position

The approach is as follows

  1. Open the existing file in READ mode and create a new file in WRITE mode
  2. Read all lines and check if we have reached the point to insert:
  3. - if we haven't, then write the existing line to the new file
  4. - if we have, write the new line to insert to the new file, followed by the current line we have just read
  5. Write the remaining lines from the original file to the new file
DECLARE newLanguage, currentLineOldFile : STRING DECLARE alreadyInserted : BOOLEAN OPENFILE ProgrammingLanguages.txt FOR READ OPENFILE ProgrammingLanguagesNew.txt FOR WRITE alreadyInserted <-- FALSE newLanguage <-- "Pseudocode" WHILE NOT EOF(ProgrammingLanguages.txt) DO READFILE ProgrammingLanguages.txt, currentLineOldFile IF alreadyInserted OR isBeforeAlphabetically(currentLineOldFile, newLanguage) THEN WRITEFILE ProgrammingLanguagesNew.txt, currentLineOldFile ELSE WRITEFILE ProgrammingLanguagesNew.txt, newLanguage WRITEFILE ProgrammingLanguagesNew.txt, currentLineOldFile alreadyInserted <-- TRUE ENDIF ENDWHILE CLOSEFILE ProgrammingLanguages.txt CLOSEFILE ProgrammingLanguagesNew.txt FUNCTION isBeforeAlphabetically(strA, strB : STRING) RETURNS BOOLEAN DECLARE index : INTEGER DECLARE currentCharA, currentCharB : CHAR strA <-- TO_LOWER(strA) strB <-- TO_LOWER(strB) index <-- 1 WHILE index <= LENGTH(strA) AND index <= LENGTH(strB) DO currentCharA <-- MID(strA, index, 1) currentCharB <-- MID(strB, index, 1) IF currentCharA < currentCharB THEN RETURN TRUE ELSE IF currentCharA > currentCharB THEN RETURN FALSE ENDIF ENDIF index <-- index + 1 ENDWHILE RETURN TRUE ENDFUNCTION

25

Checking if String is a Palindrome

One approach used to check if a string is a palindrome (the same in reverse order) is to create a new string that is the reversed version of the original string and checking it is equal to the original (e.g. by looping through in reverse order or concatenating character by character...or looping through in ascending order of character index, but prepending the current character) - this approach does work, but is less efficient in terms of both time and memory

Instead, we will go with an 'in place' approach (i.e. without creating a copy of the data) - the way we can do that is as follows:

  1. Create a character index that represents the offset from the start (left) and end (right) of the string - initialise this index to 1
  2. i.e. we will be comparing the first and last characters, the 2nd and 2nd last, the 3rd and 3rd last etc
  3. If any of the pairs don't match, then return FALSE
  4. Think of an example with an even number of characters like noon and a odd example like level - in the first example, we compare "n" with "n", then "o" with "o"...while in the 2nd example we can compare the "l"'s, then the "e"'s - we don't have to compare the remaining middle "v" character, since by virtue of it being a single character, it is a palindrome of itself. This is why the number of comparisons we have to do is the integer division of the string length divided by 2
OUTPUT IsPalindrome("Racecar") OUTPUT IsPalindrome("airplane") FUNCTION IsPalindrome(str : STRING) RETURNS BOOLEAN DECLARE maxIndex, strLength : INTEGER str <-- TO_LOWER(str) strLength <-- LENGTH(str) maxIndex <-- strLength DIV 2 FOR pos <-- 1 TO maxIndex IF MID(str, pos, 1) <> MID(str, strLength - pos + 1, 1) THEN RETURN FALSE ENDIF NEXT pos RETURN TRUE ENDFUNCTION

26

Extract Characters from Fixed-Format String

Often there will be questions where we have to use the string functions LEFT, RIGHT, MID and LENGTH to extract specific parts of a string

Consider data in the following format:

[4 character student ID][variable length student name][3 letter subject code]

An example could be: "inewIsaac NewtonPHY"

We could then extract each part using the following code - note how since 7 characters are fixed in length (4 + 3), then we can use that to calculate that the name must be the total string length minus 7

DECLARE studentID, studentName, subjectCode, line : STRING line <-- "inewIsaac NewtonPHY" studentID <-- LEFT(line, 4) studentName <-- MID(line, 5, LENGTH(line) - 7) subjectCode <-- RIGHT(line, 3) OUTPUT "StudentID: ", studentID OUTPUT "Name: ", studentName OUTPUT "Subject: ", subjectCode

27

Convert Minutes to Hours & Minutes

In recent exam papers, there have been a lot of date/time questions - it's recommended to attempt all the past paper questions and see the solutions, but something that has been asked a few times is to convert a given number of minutes like 200 into the number of hours and minutes it represents

We could do this by working out how many whole hours can be made from 200 minutes (in this case, 3 hours = 180 minutes), then subtracting 180 minutes, giving us 20 minutes remaining

It's actually possible to do it even more simply, by using just DIV and MOD

DECLARE totalMinutes, hours, minutes : INTEGER totalMinutes <-- 200 hours <-- totalMinutes DIV 60 minutes <-- totalMinutes MOD 60 OUTPUT totalMinutes, " minutes = ", hours, "h ", minutes, "m"

28

Implementing Existing Functions Yourself

Sometimes exams ask you to implement existing Cambridge pseudocode functions yourself - we will just look at 1 here, though you can also try to implement others too

For this example, let's try to convert a string (using the English alphabet) to uppercase, without using TO_UPPER

We can do this by:

  1. Declaring a variable to store out new uppercase string (and others for the current position & current char)
  2. Looping through each character in the original string
  3. If the current character is lowercase, you can convert it to uppercase by subtracting 32 from it's ASCII value, then converting that resulting integer into a character using CHR - this works since the ASCII codes for uppercase English characters are 32 less than their corresponding lowercase equivalent. Concatenate this new character with the new string
  4. If the current character isn't lowercase, then just concatenate this character with the new string - in effect, making no change
OUTPUT CUSTOM_TO_UPPER("heLlo woRld") FUNCTION CUSTOM_TO_UPPER(origStr : STRING) RETURNS STRING DECLARE pos : INTEGER DECLARE currentChar : CHAR DECLARE newStr : STRING newStr <-- "" FOR pos <-- 1 TO LENGTH(origStr) currentChar <-- MID(origStr, pos, 1) IF currentChar >= 'a' AND currentChar <= 'z' THEN newStr <-- newStr & CHR(ASC(currentChar) - 32) ELSE newStr <-- newStr & currentChar ENDIF NEXT pos RETURN newStr ENDFUNCTION

29

Factorial

To calculate factorial, we can use either an iterative or recursive approach - since recursion is an A2 topic, then just the iterative approach will be expected

Note how we can start the loop from 2, since doing 1 * 1 is redundant - if an argument of 0 or 1 is entered, max will be less than the starting index, hence the loop body will never be entered

For simplicity, it's not handled here, but you could output an error message and return -1 or something if a negative value is passed in

DECLARE index : INTEGER FOR index <-- 0 TO 50 OUTPUT "Iterative ", index, ": ", factorial_iterative(index) OUTPUT "Recursive ", index, ": ", factorial_recursive(index) OUTPUT "---" NEXT index FUNCTION factorial_iterative(max : INTEGER) RETURNS INTEGER DECLARE sum, index : INTEGER sum <-- 1 FOR index <-- 2 TO max sum <-- sum * index NEXT index RETURN sum ENDFUNCTION FUNCTION factorial_recursive(n : INTEGER) RETURNS INTEGER IF n = 0 OR n = 1 THEN RETURN 1 ELSE RETURN n * factorial_recursive(n - 1) ENDIF ENDFUNCTION

30

Fibonacci

This has never actually come up in a past paper - though it's a famous series, common programming task and could potentially come up in a future paper, so I will include it anyway

CALL fibonacci(100) PROCEDURE fibonacci(amount : INTEGER) DECLARE n1, n2, temp, index : INTEGER n1 <-- 0 n2 <-- 1 OUTPUT n1 OUTPUT n2 FOR index <-- 1 TO amount - 2 temp <-- n2 n2 <-- n2 + n1 n1 <-- temp OUTPUT n2 NEXT index ENDPROCEDURE