Computer Science 9618/41 — May/June 2024
Cambridge A-Level · Practical · worked solutions for every part, with the mark scheme
Topics Programming Paradigms (Procedural and Object-oriented) · Algorithms and Abstract Data Types · File Processing and Exception Handling
A program needs to take integer numbers as input, sort the numbers and then search for a specific number.
Open the evidence document evidence.doc
Make sure that your name, centre number and candidate number will appear on every page of this document. This document must contain your answers to each question.
Save this evidence document in your work area as:
evidence_ followed by your centre number_candidate number, for example: evidence_zz999_9999
A class declaration can be used to declare a record.
If the programming language used does not support arrays, a list can be used instead.
The integer numbers will be stored in the global 1D array, DataStored, with space for up to 20 integers.
The global variable NumberItems stores the quantity of items the array contains.
Write program code to declare DataStored and NumberItems.
Save your program as Question1_J24.
Copy and paste the program code into part 1(a) in the evidence document.
Answer
DataStored = [0 for x in range(20)]
NumberItems = 0
See program code
Background Concept
A global variable is declared outside all procedures and functions so that it can be accessed from different parts of the program. In this question, one global structure is needed to hold up to 20 integers, and another global variable is needed to record how many of those positions are currently in use. In Python, a fixed-size array is usually represented using a list that is pre-filled with default values.
Understanding the Question
You are asked only to declare DataStored and NumberItems. The stem says DataStored must be a 1D array with space for up to 20 integers, and NumberItems must store how many values are currently present. So the answer needs one declaration for the storage structure and one for the count.
Approach
Use a Python list of length 20, filled with 0, so indexed storage can be used later in the program. Then set NumberItems to 0 so the program starts with no valid items stored.
Step-by-Step Reasoning
DataStored = [0 for x in range(20)] creates 20 positions. Each starts as 0, which is just an initial placeholder value. This is useful because later procedures will store numbers into positions 0 to NumberItems - 1.
NumberItems = 0 creates the count variable and gives it a sensible starting value. The main program in the question also says that 0 is stored in NumberItems before input begins, so this matches the required setup.
Key Takeaways
You should be able to declare global data that will be shared by several procedures. You should also recognise that an array-like structure needs both the storage itself and a separate variable to track how many positions are actually being used.
Common Mistakes
A common mistake is declaring an empty list with no space reserved, then trying to assign by index later. Another mistake is forgetting NumberItems, which means later loops do not know how many valid values are present.
Things to Be Careful About
Make sure the structure has 20 positions, not 19 or 21. Also keep the identifier names exactly as given: DataStored and NumberItems.
The procedure Initialise():
- prompts the user to input the quantity of numbers the user would like to enter
- reads the input and validates it is between 1 and 20 (inclusive)
- prompts the user to input each number and stores each number in
DataStored.
Write program code for Initialise().
Save your program.
Copy and paste the program code into part 1(b) in the evidence document.
Answer
def Initialise():
global DataStored, NumberItems
NumberItems = int(input("How many numbers will you enter?"))
while NumberItems < 1 or NumberItems > 20:
NumberItems = int(input("How many numbers will you enter?"))
for Count in range(NumberItems):
DataStored[Count] = int(input("Enter number"))
See program code
Background Concept
A procedure groups a sequence of instructions so it can be called when needed. Input validation checks whether data is acceptable before the program continues. Here, the quantity of numbers must be between 1 and 20 inclusive, so a validation loop is needed before any array storage takes place.
Understanding the Question
Initialise() must do three things: ask how many numbers will be entered, keep asking until that quantity is valid, and then read that many integers into DataStored. The parent stem matters here because DataStored and NumberItems are global, so the procedure must update those global values.
Approach
First read NumberItems. Then use a while loop to reject values smaller than 1 or larger than 20. Once the quantity is valid, use a for loop that runs exactly NumberItems times, storing each input into the next array position.
Step-by-Step Reasoning
global DataStored, NumberItems is needed because the procedure is changing the shared data, not creating temporary local copies.
NumberItems = int(input("How many numbers will you enter?")) reads the first attempt.
while NumberItems < 1 or NumberItems > 20: checks the invalid cases. If the user enters 30, for example, the condition is true, so the program asks again. This matches the required validation and the sample test where 30 is rejected.
for Count in range(NumberItems): repeats once for each number to be stored. In Python, range(NumberItems) gives indexes 0 up to NumberItems - 1, which matches the positions used in the list.
DataStored[Count] = int(input("Enter number")) reads each integer and stores it in the correct position.
Key Takeaways
You should be able to use a validation loop for restricted input and then follow it with a count-controlled loop for repeated data entry. This pattern appears often in practical questions.
Common Mistakes
A common mistake is validating with and instead of or; no number can be less than 1 and greater than 20 at the same time, so that test would never reject anything. Another mistake is using range(1, NumberItems) and accidentally missing either the first or last item. Some candidates also forget to convert the input to an integer.
Things to Be Careful About
The range is inclusive, so both 1 and 20 must be accepted. The data should be stored from index 0 onward. In Python, remember that input() returns text, so int(...) is needed here.
The main program stores 0 in NumberItems, calls Initialise() and then outputs the contents of DataStored.
Write program code for the main program.
Save your program.
Copy and paste the program code into part 1(c)(i) in the evidence document.
Answer
NumberItems = 0
Initialise()
print(DataStored[0:NumberItems])
See program code
Background Concept
The main program controls the order in which procedures run. In a program like this, the sequence matters: initialise the count, gather the data, then display the stored values. When only part of an array contains valid data, output should usually be limited to that populated section.
Understanding the Question
This part says the main program stores 0 in NumberItems, calls Initialise(), and then outputs the contents of DataStored. The important detail is that only the entered items should be shown, not all 20 positions.
Approach
Set NumberItems to 0 first. Call Initialise() so the user can enter the data. Then print only DataStored[0:NumberItems], which is the slice containing the valid numbers.
Step-by-Step Reasoning
NumberItems = 0 matches the instruction in the question. It ensures the program starts with no active items.
Initialise() then runs the procedure written in part (b). That procedure validates the quantity and stores the input integers into the first NumberItems positions of the list.
print(DataStored[0:NumberItems]) outputs only the values that were actually entered. If you printed the whole list, you would also display unused zeros, which does not match the evidence shown in the mark scheme.
Key Takeaways
You should understand how the main program coordinates procedure calls and why output is often limited to the used portion of an array or list.
Common Mistakes
A common mistake is printing DataStored by itself, which would show all 20 positions. Another mistake is forgetting to call Initialise(), so no user data is stored before printing.
Things to Be Careful About
Python list slicing excludes the upper bound, so DataStored[0:NumberItems] correctly includes positions 0 to NumberItems - 1. Keep the order of statements correct: output must come after the procedure call.
Test your program by inputting the following data in the order given:
30
5
3
9
4
1
2
Take a screenshot of the output(s).
Save your program.
Copy and paste the screenshot into part 1(c)(ii) in the evidence document.
Answer
Input sequence: 30, 5, 3, 9, 4, 1, 2
How many numbers will you enter?30
How many numbers will you enter?5
Enter number3
Enter number9
Enter number4
Enter number1
Enter number2
[3, 9, 4, 1, 2]
See expected console output
Background Concept
Testing console programs often means following the prompts, the user's responses and the final output exactly. If validation is present, invalid data produces extra prompts before the program can continue.
Understanding the Question
You are not writing new code here. You are showing what the program would display when the user enters 30, then 5, then the five numbers 3, 9, 4, 1, 2. The first value is deliberately invalid because the allowed quantity is only 1 to 20.
Approach
Trace the program in order. First, the quantity is read and checked. Because 30 is too large, the program asks again. Once 5 is entered, the program reads five numbers and then prints the populated section of the list.
Step-by-Step Reasoning
The first prompt appears:
How many numbers will you enter?
The user enters 30.
Validation checks whether the quantity is less than 1 or greater than 20. Since 30 > 20, it is rejected, so the same prompt appears again.
The user then enters 5, which is valid. The program now runs the input loop five times:
- first stores
3 - second stores
9 - third stores
4 - fourth stores
1 - fifth stores
2
After that, the main program prints DataStored[0:NumberItems], so the output is [3, 9, 4, 1, 2].
Key Takeaways
You should be able to trace how validation affects output and how a list slice produces only the values that were actually entered.
Common Mistakes
A common mistake is forgetting the repeated prompt after the invalid 30. Another is giving the list in sorted order, even though sorting has not happened yet in part (c).
Things to Be Careful About
This output is from the program before any bubble sort is added. Also, the displayed list contains spaces after commas in normal Python list output.
The procedure BubbleSort() uses a bubble sort to sort the data in DataStored into ascending numerical order.
Write program code for BubbleSort().
Save your program.
Copy and paste the program code into part 1(d)(i) in the evidence document.
Answer
def BubbleSort():
global DataStored, NumberItems
for x in range(NumberItems - 1):
for y in range(NumberItems - x - 1):
if DataStored[y] > DataStored[y + 1]:
Temp = DataStored[y]
DataStored[y] = DataStored[y + 1]
DataStored[y + 1] = Temp
See program code
Background Concept
Bubble sort is a comparison sort that repeatedly scans through a list, comparing adjacent items and swapping them if they are in the wrong order. After each full pass, the largest remaining unsorted value has moved to its correct position at the end of the list. Because of that, each later pass can be one comparison shorter.
Understanding the Question
You must write a procedure BubbleSort() that sorts the values in DataStored into ascending numerical order. The array length to use is not 20 every time; it is the current value of NumberItems, because only those positions contain valid data.
Approach
Use two loops: an outer loop for the number of passes, and an inner loop for adjacent comparisons within each pass. If a left value is greater than the one to its right, swap them. Repeat until the list is sorted.
Step-by-Step Reasoning
global DataStored, NumberItems allows the procedure to sort the shared list.
for x in range(NumberItems - 1): controls the passes. If there are n items, bubble sort needs at most n - 1 passes.
for y in range(NumberItems - x - 1): compares adjacent positions. The - x shortens the inner loop each pass because the largest items have already bubbled to the end.
if DataStored[y] > DataStored[y + 1]: checks whether two neighbouring values are out of ascending order.
The three assignment statements using Temp perform a swap:
- store the left value temporarily
- move the right value left
- put the saved value on the right
After all passes finish, the first NumberItems positions are in ascending order.
Key Takeaways
You should know the standard bubble sort pattern: nested loops, adjacent comparison and swap when needed. You should also recognise why the inner loop becomes shorter on later passes.
Common Mistakes
Common mistakes include comparing in the wrong direction, which would sort descending instead of ascending, and using the wrong loop bounds so y + 1 goes out of range. Another mistake is forgetting one part of the swap, which loses a value.
Things to Be Careful About
The sort must use NumberItems, not the full size 20. Also, the array must be sorted in ascending numerical order, so the condition should be > before swapping.
Write program code to amend the main program to call BubbleSort() and then output the contents of DataStored.
Save your program.
Copy and paste the program code into part 1(d)(ii) in the evidence document.
Answer
NumberItems = 0
Initialise()
BubbleSort()
print(DataStored[0:NumberItems])
See program code
Background Concept
After a sorting procedure has been written, the main program must call it at the correct point. Program flow is important: data must be input before it can be sorted, and it must be sorted before sorted output can be displayed.
Understanding the Question
This part asks you to amend the main program so that BubbleSort() is called and the sorted contents are then output. That means the sort must appear after Initialise() and before the print statement.
Approach
Keep the same main-program structure from part (c), but insert BubbleSort() before the output line.
Step-by-Step Reasoning
NumberItems = 0 sets the starting count.
Initialise() reads and stores the user's values.
BubbleSort() then rearranges the first NumberItems values in ascending order.
print(DataStored[0:NumberItems]) displays only the used portion, which is now sorted.
Key Takeaways
You should be comfortable integrating separately written procedures into a main program in the correct sequence.
Common Mistakes
A common mistake is printing the array before calling BubbleSort(), which would still show the original unsorted order. Another is printing the whole list and showing unused zeros.
Things to Be Careful About
The new procedure call must be inserted between input and output. Keep the slice DataStored[0:NumberItems] so the evidence matches the required display.
Test your program by inputting the following data in the order given:
5
3
9
4
1
2
Take a screenshot of the output(s).
Save your program.
Copy and paste the screenshot into part 1(d)(iii) in the evidence document.
Answer
Input sequence: 5, 3, 9, 4, 1, 2
How many numbers will you enter?5
Enter number3
Enter number9
Enter number4
Enter number1
Enter number2
[1, 2, 3, 4, 9]
See expected console output
Background Concept
Testing after adding a sort checks that the data entry still works and that the sorting algorithm changes the order correctly. With bubble sort, the final displayed list should be in ascending order.
Understanding the Question
You must show the output when the user enters that there will be 5 numbers, followed by 3, 9, 4, 1, 2. Because the main program now calls BubbleSort(), the displayed list should be sorted before it is printed.
Approach
Store the five values, apply bubble sort, then write the exact list that Python would print.
Step-by-Step Reasoning
The values entered are [3, 9, 4, 1, 2] initially.
After sorting into ascending order, they become [1, 2, 3, 4, 9].
That sorted list is what print(DataStored[0:NumberItems]) displays.
Key Takeaways
You should be able to distinguish between the original input order and the post-sort output order.
Common Mistakes
A common mistake is giving the unsorted list from part (c) again. Another is omitting one of the values after sorting.
Things to Be Careful About
The output shown here is after the bubble sort has been added. Keep the list format exactly as Python displays it, with brackets and commas.
The function BinarySearch():
- takes the integer parameter
DataToFindto search for in the array - performs an iterative binary search on the array
DataStored - returns the index where
DataToFindis found inDataStored. IfDataToFindis not found, the function returns -1.
Write program code for the iterative function BinarySearch().
Save your program.
Copy and paste the program code into part 1(e)(i) in the evidence document.
Answer
def BinarySearch(DataToFind):
global DataStored, NumberItems
LowerBound = 0
UpperBound = NumberItems - 1
while LowerBound <= UpperBound:
Middle = (LowerBound + UpperBound) // 2
if DataStored[Middle] == DataToFind:
return Middle
elif DataStored[Middle] < DataToFind:
LowerBound = Middle + 1
else:
UpperBound = Middle - 1
return -1
See program code
Background Concept
Binary search is an efficient searching algorithm for sorted data. Instead of checking each item one by one, it repeatedly looks at the middle item and discards half of the remaining search area. An iterative binary search uses a loop with lower and upper bounds rather than recursive function calls.
Understanding the Question
The function BinarySearch() takes one integer parameter, DataToFind. It must search the sorted array DataStored and return the index where the value is found. If it is not present, it must return -1. The word "iterative" is important: this must be written with a loop, not recursion.
Approach
Start with the whole populated part of the array as the search interval: lower bound 0, upper bound NumberItems - 1. Repeatedly calculate the middle index. If the middle value is the target, return that index. If the target is larger, search the upper half next; if smaller, search the lower half next. If the bounds cross, the value is not present.
Step-by-Step Reasoning
LowerBound = 0 sets the first valid index of the used part of the list.
UpperBound = NumberItems - 1 sets the last valid index.
while LowerBound <= UpperBound: means there is still at least one possible position left to check.
Middle = (LowerBound + UpperBound) // 2 finds the midpoint index using integer division.
If DataStored[Middle] == DataToFind, the search succeeds immediately, so the function returns Middle.
If DataStored[Middle] < DataToFind, the target must be to the right in a sorted ascending list, so the new lower bound becomes Middle + 1.
Otherwise, the target must be to the left, so the new upper bound becomes Middle - 1.
If the loop ends without finding the item, the function returns -1, which is a standard sentinel value meaning "not found".
Key Takeaways
You should know that binary search only works correctly on sorted data and that the essential mechanics are midpoint calculation plus shrinking bounds.
Common Mistakes
A very common mistake is trying to binary search unsorted data. Another is updating a bound to Middle instead of Middle + 1 or Middle - 1, which can cause an infinite loop. Some candidates also return -1 too early from inside the loop.
Things to Be Careful About
This question uses Python list indexes, so the first index is 0. Make sure the function returns the index, not the value found. Also ensure integer division uses //.
Write program code to amend the main program to:
- take a number as input from the user
- call
BinarySearch()with the number input - output the value returned from the function call as its parameter.
Save your program.
Copy and paste the program code into part 1(e)(ii) in the evidence document.
Answer
NumberItems = 0
Initialise()
BubbleSort()
print(DataStored[0:NumberItems])
DataToFind = int(input("Enter a number to find"))
print(BinarySearch(DataToFind))
See program code
Background Concept
A function is called with an argument, processes that input and returns a value. The main program can then use or display that returned value. Here, the search key comes from the user and the binary search function returns either an index or -1.
Understanding the Question
You must amend the main program again so that, after sorting and displaying the array, it asks the user for a number to find, calls BinarySearch() with that number, and outputs the returned value.
Approach
Keep the input and sorting steps from the earlier parts. Then add one input statement to get the search value and one output statement to display the result of BinarySearch().
Step-by-Step Reasoning
NumberItems = 0, Initialise() and BubbleSort() are still needed because the search should happen on the populated, sorted data.
print(DataStored[0:NumberItems]) shows the sorted list before the search result, which matches the evidence in the mark scheme.
DataToFind = int(input("Enter a number to find")) reads the target value from the user.
print(BinarySearch(DataToFind)) calls the function and prints its return value directly. If the value is present, the index is shown; otherwise -1 is shown.
Key Takeaways
You should be able to add a function call into an existing main program and pass user input into that function.
Common Mistakes
A common mistake is forgetting to sort before using binary search. Another is reading the search value as text and not converting it to an integer.
Things to Be Careful About
The prompt text should match the program style used elsewhere. Also, the question asks to output the value returned from the function call, so the result must be printed, not just stored.
Test your program twice with the following inputs:
Test 1: 5 1 6 2 8 10 2
Test 2: 5 1 6 2 8 10 7
Take a screenshot of the output(s).
Save your program.
Copy and paste the screenshot into part 1(e)(iii) in the evidence document.
Answer
Test 1 input sequence: 5, 1, 6, 2, 8, 10, 2
How many numbers will you enter?5
Enter number1
Enter number6
Enter number2
Enter number8
Enter number10
[1, 2, 6, 8, 10]
Enter a number to find?2
1
Test 2 input sequence: 5, 1, 6, 2, 8, 10, 7
How many numbers will you enter?5
Enter number1
Enter number6
Enter number2
Enter number8
Enter number10
[1, 2, 6, 8, 10]
Enter a number to find?7
-1
See expected console output
Background Concept
A full test of a search routine should include both a successful search and an unsuccessful one. For binary search, the array must first be sorted, then the returned value is either the index of the matching item or a sentinel such as -1.
Understanding the Question
You must show the outputs for two runs of the finished program. In both tests, the numbers entered are 1, 6, 2, 8, 10, which are then sorted. In Test 1 the search target is 2, which exists. In Test 2 the search target is 7, which does not exist.
Approach
First determine the sorted array. Then apply binary search mentally to decide what index is returned for 2 and what happens for 7.
Step-by-Step Reasoning
After input, the values are [1, 6, 2, 8, 10].
After BubbleSort(), they become [1, 2, 6, 8, 10].
For Test 1, the user searches for 2. In this sorted list, 2 is at index 1 using Python's 0-based indexing, so the program outputs 1.
For Test 2, the user searches for 7. The search narrows the range but never finds a match, so the function returns -1.
That is why the two final outputs are 1 and -1.
Key Takeaways
You should be able to test search algorithms using both normal and not-found cases, and you should understand that list indexes in Python start at 0.
Common Mistakes
A common mistake is answering 2 for the first test because the value searched for is 2; the function returns the index, not the value. Another is giving index 2 because of 1-based counting, but Python uses 0-based indexing.
Things to Be Careful About
The output list is sorted before the search happens. Also, -1 is not an index here; it is the special return value meaning the item was not found.
A computer program will store data about trees.
The user can enter their requirements for a tree and a suitable tree will be selected.
The program is written using object-oriented programming.
The class Tree stores data about the trees.
One source file is used to answer Question 2. The file is called Trees.txt
| Tree | |
|---|---|
TreeName : STRING | stores the name of the tree |
HeightGrowth : INTEGER | stores the number of cm the tree will grow each year |
MaxHeight : INTEGER | stores the maximum height in cm that the tree will grow |
MaxWidth : INTEGER | stores the maximum width in cm that the tree will grow |
Evergreen : STRING | stores whether the tree keeps its leaves as "Yes", or loses its leaves as "No" |
Constructor() | initialises TreeName, HeightGrowth, MaxHeight, MaxWidth and Evergreen to its parameter values |
GetTreeName() | returns the name of the tree |
GetGrowth() | returns the number of cm the tree will grow each year |
GetMaxHeight() | returns the maximum height in cm that the tree will grow |
GetMaxWidth() | returns the maximum width in cm that the tree will grow |
GetEvergreen() | returns whether the tree keeps its leaves or loses its leaves |
Write program code to declare the class Tree and its constructor.
Do not declare the other methods.
Use the appropriate constructor for your programming language.
All attributes must be private.
If you are writing in Python, include attribute declarations using comments.
Save your program as Question2_J24.
Copy and paste the program code into part 2(a)(i) in the evidence document.
Answer
class Tree:
# __TreeName : str
# __HeightGrowth : int
# __MaxHeight : int
# __MaxWidth : int
# __Evergreen : str
def __init__(self, TreeName, HeightGrowth, MaxHeight, MaxWidth, Evergreen):
self.__TreeName = TreeName
self.__HeightGrowth = HeightGrowth
self.__MaxHeight = MaxHeight
self.__MaxWidth = MaxWidth
self.__Evergreen = Evergreen
See program code
Background Concept
In object-oriented programming, a class is a template for creating objects. Each object stores its own data in attributes and can provide methods to work with that data. A constructor is the method that runs when a new object is created. Its job is usually to receive parameter values and use them to initialise the attributes.
This question also requires the attributes to be private. In Python, this is usually shown with a double underscore prefix such as __TreeName. That indicates the data should be accessed through methods rather than directly from outside the class. In Cambridge Paper 4, Python answers often include commented attribute declarations because Python does not require separate declarations before use.
Understanding the Question
You are asked only for the declaration of the Tree class and its constructor. You must not add the other methods yet. The class needs to store five pieces of data from the stem:
- tree name
- yearly height growth
- maximum height
- maximum width
- evergreen status
Because the question explicitly says all attributes must be private, each attribute must be stored as a private instance variable.
Approach
The simplest correct Python answer is:
- declare
class Tree - add comment lines showing the private attributes
- write
__init__with one parameter for each item of tree data - copy each parameter into the corresponding private attribute using
self
That fully satisfies the constructor requirement.
Step-by-Step Reasoning
class Tree: creates the class.
The comment lines are included because the question specifically says that if Python is used, attribute declarations should be shown using comments. They make clear that the object stores five private fields.
The constructor in Python is def __init__(...). The first parameter must be self, which refers to the current object being created.
The remaining parameters match the values described in the table:
TreeNameHeightGrowthMaxHeightMaxWidthEvergreen
Each assignment such as self.__TreeName = TreeName stores the passed value inside the new object. The double underscore keeps the attribute private.
No getter methods should appear here, because the part explicitly says not to declare the other methods.
Key Takeaways
- A constructor initialises object attributes when an object is created.
- Private attributes in Python are commonly written with a double underscore prefix.
- In Paper 4 Python answers, comment-based attribute declarations are often used when requested.
Common Mistakes
- Making attributes public, for example using
self.TreeNameinstead ofself.__TreeName. - Forgetting one of the five attributes from the class table.
- Writing getter methods in this part even though the question says not to.
- Omitting
selffrom the constructor parameter list.
Things to Be Careful About
Use the exact class name Tree and keep the attribute meanings consistent with the stem. Make sure the constructor stores all five values, not just some of them. In Python, the constructor must be named __init__, not Constructor().
The get methods GetTreeName(), GetGrowth(), GetMaxHeight(), GetMaxWidth() and GetEvergreen() each return the relevant attribute.
Write program code for the get methods.
Save your program.
Copy and paste the program code into part 2(a)(ii) in the evidence document.
Answer
def GetTreeName(self):
return self.__TreeName
def GetGrowth(self):
return self.__HeightGrowth
def GetMaxHeight(self):
return self.__MaxHeight
def GetMaxWidth(self):
return self.__MaxWidth
def GetEvergreen(self):
return self.__Evergreen
See program code
Background Concept
A getter method returns the value of a private attribute. Getters are part of encapsulation: the data is hidden inside the object, and outside code accesses it through methods rather than directly.
In this question, the class stores its fields privately, so other parts of the program must use methods such as GetTreeName() and GetMaxHeight() to read those values.
Understanding the Question
This part tells you exactly what each get method should do: return the relevant attribute. That means no calculations, no printing, and no input. Each method simply returns one field from the Tree object.
Because part (i) already created the private attributes, these methods must refer to those same private names.
Approach
For each required getter:
- use the exact method name from the question
- include
selfas the parameter - return the correct private attribute
There are five methods, one for each stored value.
Step-by-Step Reasoning
GetTreeName() returns self.__TreeName, which is the stored tree name.
GetGrowth() returns self.__HeightGrowth, the annual growth amount.
GetMaxHeight() returns self.__MaxHeight.
GetMaxWidth() returns self.__MaxWidth.
GetEvergreen() returns self.__Evergreen.
The key point is that each method returns a value. It does not display it with print(), because later procedures may need to use the value inside conditions or calculations.
Key Takeaways
- Getter methods allow code outside the class to read private data safely.
- A getter usually contains just one
returnstatement. - The method names and the returned attributes must match exactly.
Common Mistakes
- Using
print()instead ofreturn. - Returning the wrong attribute from a method.
- Forgetting
selfin the parameter list. - Accessing public names that do not exist instead of the private attributes created in part (i).
Things to Be Careful About
Keep the method names exactly as given: GetTreeName(), GetGrowth(), GetMaxHeight(), GetMaxWidth() and GetEvergreen(). In Python, the return statements must use the same private attribute names as the constructor, including the double underscores.
The text file Trees.txt stores data about 9 trees.
The data in the file is stored in the format:
Tree name,Height growth each year,Maximum height,Maximum width,Evergreen
For example, the first row of data is:
Beech,30,400,200,No
The tree is a Beech. It can grow 30 cm each year. It has a maximum height of 400 cm. It has a maximum width of 200 cm. It is not evergreen (it loses its leaves).
The function ReadData():
- creates an array of type
Tree - reads the data from the file
- raises an exception if the file is not found
- creates a new object of type
Treefor each tree in the file - appends each object to the array
- returns the array.
Write program code for ReadData().
Save your program.
Copy and paste the program code into part 2(b) in the evidence document.
Answer
def ReadData():
TreeArray = []
try:
TreeFile = open("Trees.txt", "r")
for Line in TreeFile:
TreeData = Line.strip().split(",")
NewTree = Tree(TreeData[0], int(TreeData[1]), int(TreeData[2]), int(TreeData[3]), TreeData[4])
TreeArray.append(NewTree)
TreeFile.close()
return TreeArray
except FileNotFoundError:
raise
See program code
Background Concept
A file-reading function for Paper 4 usually performs several standard jobs: open the file, read each record, separate the fields, convert data to the correct types, store the data in a suitable structure, then return that structure.
Here the structure is an array of Tree objects. In Python, that is naturally a list containing object references. Because the file is plain text and each line contains one tree, the file is processed sequentially from top to bottom.
Exception handling is relevant because trying to open a file that does not exist causes a FileNotFoundError.
Understanding the Question
The stem gives the exact file format:
Tree name,Height growth each year,Maximum height,Maximum width,Evergreen
and even shows a sample line:
Beech,30,400,200,No
So each line has five comma-separated fields. The function must:
- create an array of
Tree - read the file
- create one
Treeobject per line - append each object to the array
- return the array
- raise an exception if the file is not found
That tells you the routine must both parse data and construct objects.
Approach
A good method is:
- start with an empty list
- try to open
Trees.txt - loop through each line in the file
- remove the line ending and split by commas
- convert numeric fields to integers
- pass the five values into the
Treeconstructor - append the new object to the list
- close the file and return the list
- if opening fails, re-raise the
FileNotFoundError
Step-by-Step Reasoning
TreeArray = [] creates the array that will hold the Tree objects.
The try block is used because the open operation may fail.
open("Trees.txt", "r") opens the file for reading. The file data supplied in the question contains 9 lines, one per tree.
The for Line in TreeFile: loop reads each line in turn.
Line.strip().split(",") does two jobs:
strip()removes the newline at the end of the linesplit(",")separates the line into five fields
For the first row, this produces something equivalent to:
TreeData[0] = "Beech"TreeData[1] = "30"TreeData[2] = "400"TreeData[3] = "200"TreeData[4] = "No"
The middle three values must be integers, so they are converted with int(...) before creating the object.
NewTree = Tree(...) calls the constructor from part (a) to make one object for that record.
TreeArray.append(NewTree) stores the object in the list.
After all lines have been processed, the file is closed and the list is returned.
The except FileNotFoundError: block uses raise so the exception is still raised, which matches the question requirement.
Key Takeaways
- Text files often need
split()to separate fields. - Object arrays are built by repeatedly constructing objects and appending them to a list.
- Numeric strings from files must be converted before numeric use.
FileNotFoundErroris the standard Python exception for a missing file.
Common Mistakes
- Forgetting to convert the numeric fields to integers.
- Appending the raw list of strings instead of a
Treeobject. - Returning before the loop finishes.
- Using the wrong file name or wrong open mode.
- Catching the exception but not re-raising it, which would fail the stated requirement.
Things to Be Careful About
The tree name Magnolia Grandiflora contains a space, but that is fine because the separator is a comma, not a space. Only indices 1, 2 and 3 should be converted to integers. Index 4 stays as the string Yes or No. Make sure the constructor arguments are passed in the correct order.
The procedure PrintTrees() takes a Tree object as a parameter and outputs the tree’s name, height growth each year, maximum height, maximum width and whether it is evergreen.
The output message changes depending on whether it is evergreen.
If it is evergreen, it is in the format:
TreeName has a maximum height MaxHeight a maximum width MaxWidth and grows HeightGrowth cm a year. It does not lose its leaves.
If it is not evergreen, it is in the format:
TreeName has a maximum height MaxHeight a maximum width MaxWidth and grows HeightGrowth cm a year. It loses its leaves each year.
Write program code for PrintTrees().
Save your program.
Copy and paste the program code into part 2(c) in the evidence document.
Answer
def PrintTrees(ThisTree):
if ThisTree.GetEvergreen() == "Yes":
print(f"{ThisTree.GetTreeName()} has a maximum height {ThisTree.GetMaxHeight()} a maximum width {ThisTree.GetMaxWidth()} and grows {ThisTree.GetGrowth()} cm a year. It does not lose its leaves.")
else:
print(f"{ThisTree.GetTreeName()} has a maximum height {ThisTree.GetMaxHeight()} a maximum width {ThisTree.GetMaxWidth()} and grows {ThisTree.GetGrowth()} cm a year. It loses its leaves each year.")
See program code
Background Concept
A procedure can receive an object as a parameter and then use that object's methods to access its data. Because the Tree attributes are private, PrintTrees() should not access attributes directly. It should call the getter methods instead.
Selection is also needed here because there are two possible output formats depending on whether the tree is evergreen.
Understanding the Question
You are given the exact wording required for two cases:
- evergreen tree
- not evergreen tree
So the job of PrintTrees() is to take one Tree object, inspect its evergreen value, and print the correct sentence using the stored name, maximum height, maximum width and yearly growth.
Approach
The natural structure is:
- define a procedure with one parameter for the
Treeobject - check
GetEvergreen() - if it is
"Yes", print the evergreen message - otherwise, print the non-evergreen message
Both print statements should use the same getters, changing only the final sentence.
Step-by-Step Reasoning
def PrintTrees(ThisTree): creates a procedure that receives one object.
ThisTree.GetEvergreen() checks whether the object stores "Yes" or "No".
If it is "Yes", the output ends with It does not lose its leaves.
Otherwise, the output ends with It loses its leaves each year.
All the other values come from getters:
GetTreeName()for the nameGetMaxHeight()for the maximum heightGetMaxWidth()for the maximum widthGetGrowth()for yearly growth
Using an f-string makes it easy to place those values into the required sentence.
Key Takeaways
- Procedures can work with objects passed in as parameters.
- Getter methods are used to read private object data.
- A simple
ifstatement can switch between two message formats.
Common Mistakes
- Printing
YesorNodirectly instead of changing the sentence wording. - Accessing private attributes directly instead of using the getter methods.
- Mixing up height growth and maximum height.
- Leaving out one of the required pieces of data from the message.
Things to Be Careful About
The condition should compare with "Yes", because that is the stored value in the file and object. Keep the output wording consistent between both branches, changing only the evergreen sentence. The procedure takes one object, not the whole list of trees.
The main program calls ReadData(), stores the return value and calls PrintTrees() with the first object in the returned array.
Write program code for the main program.
Save your program.
Copy and paste the program code into part 2(d)(i) in the evidence document.
Answer
TreeArray = ReadData()
PrintTrees(TreeArray[0])
See program code
Background Concept
A main program coordinates previously defined routines. In Paper 4, once helper functions and procedures have been written, the main program usually just calls them in the right order and passes the right data between them.
Because ReadData() returns an array of Tree objects, the main program can store that array in a variable and then use indexing to access a specific object.
Understanding the Question
The question tells you exactly what the main program must do:
- call
ReadData() - store the return value
- call
PrintTrees()with the first object in the returned array
So there is no need for loops or extra logic here.
Approach
Use one variable to hold the returned list of Tree objects, then use index 0 to access the first object because Python lists are zero-indexed.
Step-by-Step Reasoning
TreeArray = ReadData() calls the file-reading function from part (b). After that call, TreeArray contains 9 Tree objects built from the file.
PrintTrees(TreeArray[0]) takes the first object in that list and sends it to the printing procedure from part (c).
In Python, the first element of a list is at index 0, so TreeArray[0] refers to the first tree read from the file. From Trees.txt, that first tree is Beech.
Key Takeaways
- Main programs often just sequence function and procedure calls.
- A function return value can be stored and reused later.
- Python lists are zero-indexed.
Common Mistakes
- Forgetting to store the result of
ReadData(). - Using index
1for the first element instead of0. - Passing the whole array to
PrintTrees()instead of a singleTreeobject.
Things to Be Careful About
The question asks for the first object in the returned array, so the correct Python index is 0. Make sure the function name and procedure name match the earlier parts exactly.
Test your program.
Take a screenshot of the output(s).
Save your program.
Copy and paste the screenshot into part 2(d)(ii) in the evidence document.
Answer
Beech has a maximum height 400 a maximum width 200 and grows 30 cm a year. It loses its leaves each year.
Beech has a maximum height 400 a maximum width 200 and grows 30 cm a year. It loses its leaves each year.
Background Concept
For screenshot or test-output questions, you do not write new logic. Instead, you run or mentally trace the program using the supplied data and determine exactly what the console will display.
That means combining the file contents, the main program, and the output procedure.
Understanding the Question
The main program from part (d)(i) reads the tree data and prints the first object. The reference file shows that the first row is:
Beech,30,400,200,No
So the printed object has:
- name
Beech - growth
30 - maximum height
400 - maximum width
200 - evergreen
No
Because it is not evergreen, the non-evergreen output sentence is used.
Approach
Take the first record from the file, substitute its values into the PrintTrees() sentence, and choose the correct ending based on Evergreen = No.
Step-by-Step Reasoning
ReadData() reads the first line and creates a Tree object representing Beech.
The main program passes that object to PrintTrees().
Inside PrintTrees(), GetEvergreen() returns "No", so the else branch is used.
The values inserted are:
GetTreeName()→BeechGetMaxHeight()→400GetMaxWidth()→200GetGrowth()→30
This produces the final console line shown in the answer.
Key Takeaways
- For output questions, combine the source data with the program logic.
- The branch taken depends on the stored field values.
- Exact wording matters in console output tasks.
Common Mistakes
- Choosing the evergreen sentence even though Beech has
Noin the file. - Mixing up growth and maximum height.
- Forgetting that the first record in the file is Beech.
Things to Be Careful About
Use the first object only. Do not print all trees. Keep the wording exactly consistent with the PrintTrees() procedure logic, especially the final sentence about leaves.
The procedure ChooseTree() takes an array of Tree objects as a parameter.
The procedure prompts the user to input their requirements for a tree. The user needs to enter:
- the maximum height the tree can be in cm
- the maximum width the tree can be in cm
- whether they want the tree to be evergreen, or not evergreen.
A tree meets the requirements if:
- the tree’s maximum height is not more than the user’s input
and
- the tree’s maximum width is not more than the user’s input
and
- the tree matches their evergreen input.
The procedure creates a new array of all the Tree objects that meet all the requirements.
The procedure calls PrintTrees() for each Tree object that meets all the requirements. If there are no trees that meet all the requirements, a suitable message is output.
Write program code for ChooseTree().
Save your program.
Copy and paste the program code into part 2(e)(i) in the evidence document.
Answer
def ChooseTree(TreeArray):
MatchingTrees = []
LeafChoice = input("Do you want a tree that loses its leaves (enter lose), or keeps its leaves (enter keep) ")
MaxHeight = int(input("What is the maximum tree height in cm"))
MaxWidth = int(input("What is the maximum tree width in cm"))
if LeafChoice == "keep":
EvergreenNeeded = "Yes"
else:
EvergreenNeeded = "No"
for CurrentTree in TreeArray:
if CurrentTree.GetMaxHeight() <= MaxHeight and CurrentTree.GetMaxWidth() <= MaxWidth and CurrentTree.GetEvergreen() == EvergreenNeeded:
MatchingTrees.append(CurrentTree)
PrintTrees(CurrentTree)
if len(MatchingTrees) == 0:
print("No trees meet your requirements")
See program code
Background Concept
This is a filtering problem. A program has a collection of objects and must select only those whose attribute values satisfy given conditions. In Python, a common way to do this in exam code is a linear traversal of the list with an if statement containing all required tests.
Because the data is stored as Tree objects with private attributes, the procedure must use the getter methods when checking values.
Understanding the Question
The user gives three requirements:
- maximum allowed height
- maximum allowed width
- whether the tree should be evergreen or not
A tree is suitable only if all three conditions are satisfied. The procedure must also create a new array containing all suitable trees and call PrintTrees() for each one. If none are suitable, a message must be shown.
Approach
The best structure is:
- input the user requirements
- convert the evergreen-style input into the stored file format
YesorNo - create an empty list for matching trees
- loop through every tree in the input array
- test all three conditions together using
and - if the tree matches, append it to the list and print it
- after the loop, if the list is empty, print a no-match message
Step-by-Step Reasoning
MatchingTrees = [] creates the new array required by the question.
The procedure asks for the three user requirements. The height and width are converted to integers because comparisons like <= must be numeric.
The example solution uses keep and lose as user-friendly inputs, then maps them to the stored values:
keepbecomesYes- anything else here is treated as
No
That matters because the file and object store evergreen as Yes or No, not as keep or lose.
The for CurrentTree in TreeArray: loop checks every tree object.
The condition uses three tests joined by and:
GetMaxHeight() <= MaxHeightGetMaxWidth() <= MaxWidthGetEvergreen() == EvergreenNeeded
A tree must satisfy all three at the same time.
If it does, the object is appended to MatchingTrees, then PrintTrees(CurrentTree) outputs its details.
After the loop, len(MatchingTrees) == 0 checks whether no tree met the conditions. If so, a suitable message is printed.
Key Takeaways
- Filtering a list of objects usually means a loop plus a multi-condition
ifstatement. - Build a new list of matches if later parts of the program may need the selected objects.
- When stored values use a different format from user input, convert one form to the other before comparing.
Common Mistakes
- Using
orinstead ofand, which would allow trees that meet only some requirements. - Forgetting to append matching trees to the new array.
- Comparing
keepdirectly withYeswithout converting it. - Printing the no-match message inside the loop instead of after the loop.
Things to Be Careful About
The wording says the tree's maximum height and width must be not more than the user's input, so the comparison must be <=, not < and not >=. Use getter methods, not direct attribute access. Keep the no-match test until after all trees have been checked.
The procedure ChooseTree() needs amending. After the procedure has output the list of trees that meet all the requirements, the procedure needs to:
- take as input the name of one of the trees that the user would like to buy from those that meet all the requirements
- take as input the height of the tree in cm when it is bought
- calculate and output how many years it will take the tree to grow to its maximum height.
For example, the user inputs the tree, Beech. The tree’s height is 40 cm when bought. The tree will take 12 years to reach its maximum height of 400 cm.
Write program code to amend ChooseTree().
Save your program.
Copy and paste the program code into part 2(e)(ii) in the evidence document.
Answer
if len(MatchingTrees) > 0:
TreeChoice = input("Enter the name of the tree you want ")
StartHeight = int(input("Enter the height of the tree you would like to care with in cm"))
for CurrentTree in MatchingTrees:
if CurrentTree.GetTreeName() == TreeChoice:
Years = (CurrentTree.GetMaxHeight() - StartHeight) / CurrentTree.GetGrowth()
print(f"Your tree should be full height in approximately {Years} years")
break
See program code
Background Concept
Once a filtered list has been produced, a common next step is to search within that smaller list for one chosen item. Because the list is not sorted by name and is likely small, a linear search is the appropriate method.
The years-to-maximum-height calculation is based on:
remaining height needed divided by yearly growth
So if a tree still needs 150 cm and grows 40 cm each year, it will take years.
Understanding the Question
This part says the existing ChooseTree() procedure must be amended after it has listed the suitable trees. The program must then:
- ask the user which of those trees they want
- ask for its starting height
- calculate how many years it will take to reach maximum height
- output the result
The important phrase is "from those that meet all the requirements". That means the search should be done in the MatchingTrees list, not in the full original tree array.
Approach
Add code after the matching trees have been displayed:
- only continue if there is at least one suitable tree
- input the chosen tree name
- input the starting height
- linearly search
MatchingTreesfor that name - when found, calculate years using maximum height minus starting height, divided by yearly growth
- print the answer and stop searching
Step-by-Step Reasoning
The guard if len(MatchingTrees) > 0: prevents the program from asking the user to choose a tree when none were found.
TreeChoice stores the selected name.
StartHeight is converted to an integer because it is numeric input.
The loop goes through each tree in MatchingTrees. For each one, the name is checked with GetTreeName().
When the chosen tree is found, the calculation is:
- remaining growth needed =
GetMaxHeight() - StartHeight - years = remaining growth needed divided by
GetGrowth()
For the sample test in the question, Blue Conifer has maximum height 250 and growth 40. Starting at 100 cm gives:
- remaining growth = 250 - 100 = 150
- years = 150 / 40 = 3.75
That is why the output is approximately 3.75 years.
break stops the loop after the correct tree has been found.
Key Takeaways
- Use a linear search when checking a small unsorted list.
- Search the filtered results, not the original full data set.
- Real division is needed when the answer may not be a whole number.
Common Mistakes
- Searching the whole tree array instead of only the suitable trees.
- Using integer division, which would lose the decimal part.
- Forgetting to subtract the starting height before dividing.
- Asking for a tree choice even when there are no matching trees.
Things to Be Careful About
The selected tree name should match one of the printed suitable trees. Use / for real division in Python, not //. Keep the calculation in the order shown: remaining height first, then divide by yearly growth.
Write program code to amend the main program to call ChooseTrees().
Test your program with the following tree requirements:
- a maximum height of 400 cm
- a maximum width of 200 cm
- a tree that is evergreen (does not lose its leaves).
When asked for the tree selection, use the following data:
- first tree name entered is ‘Blue Conifer’
- starting height is 100 cm.
Take a screenshot of the outputs.
Save your program.
Copy and paste the screenshot into part 2(e)(iii) in the evidence document.
Answer
TreeArray = ReadData()
PrintTrees(TreeArray[0])
ChooseTree(TreeArray)
Expected output for inputs keep, 400, 200, Blue Conifer, 100:
Beech has a maximum height 400 a maximum width 200 and grows 30 cm a year. It loses its leaves each year.
Do you want a tree that loses its leaves (enter lose), or keeps its leaves (enter keep) keep
What is the maximum tree height in cm400
What is the maximum tree width in cm200
Blue Conifer has a maximum height 250 a maximum width 50 and grows 40 cm a year. It does not lose its leaves.
Green Conifer has a maximum height 300 a maximum width 150 and grows 40 cm a year. It does not lose its leaves.
Enter the name of the tree you want Blue Conifer
Enter the height of the tree you would like to care with in cm100
Your tree should be full height in approximately 3.75 years
See program code and output
Background Concept
A main program brings together the previously written procedures. For a test-output question, you must follow the exact control flow and use the supplied input values to determine what appears on screen.
This part uses:
- file input from
Trees.txt - object creation in
ReadData() - formatted output in
PrintTrees() - filtering and calculation in
ChooseTree()
Understanding the Question
The question says to amend the main program so that it calls ChooseTree(). The wording says ChooseTrees() once, but this is clearly a naming inconsistency because the procedure defined earlier is ChooseTree().
You must also test with these specific values:
- maximum height
400 - maximum width
200 - evergreen choice meaning keeps leaves
- selected tree
Blue Conifer - starting height
100
The expected output therefore depends on both the first printed tree from the original main program and the later filtering process.
Approach
The main program should still:
- read the file into
TreeArray - print the first tree
- call
ChooseTree(TreeArray)
Then trace which trees meet the new requirements.
Step-by-Step Reasoning
First, ReadData() loads all 9 trees.
PrintTrees(TreeArray[0]) prints the first one, which is Beech.
Then ChooseTree(TreeArray) runs.
The user enters that they want a tree that keeps its leaves, so the required evergreen value is Yes.
The height limit is 400 and the width limit is 200.
Now check the evergreen trees in the file:
- Holly: height 600, width 300 → too tall and too wide
- Magnolia Grandiflora: height 500, width 300 → too tall and too wide
- Photinia: height 400, width 400 → width too large
- Blue Conifer: height 250, width 50 → suitable
- Green Conifer: height 300, width 150 → suitable
So two trees are printed: Blue Conifer and Green Conifer.
The user then chooses Blue Conifer and enters starting height 100.
For Blue Conifer:
- maximum height = 250
- yearly growth = 40
- remaining growth = 250 - 100 = 150
- years = 150 / 40 = 3.75
So the final line is Your tree should be full height in approximately 3.75 years.
Key Takeaways
- Main programs often require only small amendments to incorporate new procedures.
- Test-output questions depend on exact tracing, not guesswork.
- Filtering conditions can be checked systematically against the supplied data file.
Common Mistakes
- Calling
ChooseTrees()instead ofChooseTree()because of the typo in the question text. - Forgetting that the earlier Beech output still appears before the
ChooseTree()interaction. - Including trees that exceed one of the limits.
- Using the wrong tree for the final year calculation.
Things to Be Careful About
Python list indexing means the first printed tree is still Beech. Only evergreen trees with maximum height at most 400 and maximum width at most 200 should be listed. The final year value should be 3.75, so real division must be used.
A program reads data from the user and stores the data that is valid in a linear queue.
The queue is stored as a global 1D array, QueueData, of string values. The array needs space for 20 elements.
The global variable QueueHead stores the index of the first element in the queue.
The global variable QueueTail stores the index of the last element in the queue.
The main program initialises all the elements in QueueData to a suitable null value, QueueHead to -1 and QueueTail to -1.
Write program code for the main program.
Save your program as Question3_J24.
Copy and paste the program code into part 3(a) in the evidence document.
Answer
QueueData = [""] * 20
QueueHead = -1
QueueTail = -1
See program code
Background Concept
A linear queue stores items in first-in, first-out order. In this question the queue is implemented using:
- a 1D array called
QueueData QueueHeadto point to the first itemQueueTailto point to the last item
When the queue is empty, there is no first item and no last item, so both pointers are set to a sentinel value. Here that sentinel is -1.
The array also needs each element set to a suitable null or empty value before use. Because the queue stores strings, an empty string such as "" is suitable.
Understanding the Question
This part only asks for the main-program initialisation, not the queue operations themselves.
You are told exactly what the starting state must be:
QueueDatamust have space for 20 string elements- every element must start with a null value
QueueHeadmust be-1QueueTailmust be-1
So the answer is just the code that creates that empty queue state.
Approach
Use Python list initialisation to create 20 empty string elements in one line, then assign both pointer variables to -1.
That is enough because Python programs can use top-level statements as the main program.
Step-by-Step Reasoning
QueueData = [""] * 20
""is the empty string[""] * 20creates a list containing 20 empty strings- that gives the queue 20 usable positions
QueueHead = -1
- this means there is currently no first item
- so the queue is empty
QueueTail = -1
- this means there is currently no last item
- again confirming the queue is empty
Together, these three lines create the required starting state for the rest of the program.
Key Takeaways
- A queue implemented with an array needs both storage and pointer initialisation.
-1is commonly used as a sentinel to mean "no valid index".- The null value chosen should match the data type stored in the array.
Common Mistakes
- Initialising the array with the wrong size, such as 19 or 21 elements.
- Using
0forQueueHeadorQueueTail, which would incorrectly suggest there is already an item in the queue. - Leaving the array uninitialised or using a non-string null value when the queue stores strings.
Things to Be Careful About
- The queue size must be exactly 20.
- The question says the queue stores string values, so an empty string is an appropriate null value.
QueueHeadandQueueTailmust both start at-1, not one at0and the other at-1.
The function Enqueue() takes the data to insert into the queue as a parameter.
If the queue is not full, it inserts the parameter in the queue, updates the appropriate pointer(s) and returns TRUE. If the queue is full, it returns FALSE.
Write program code for Enqueue().
Save your program.
Copy and paste the program code into part 3(b) in the evidence document.
Answer
def Enqueue(DataToAdd):
global QueueData, QueueHead, QueueTail
if QueueTail == 19:
return False
if QueueHead == -1:
QueueHead = 0
QueueTail += 1
QueueData[QueueTail] = DataToAdd
return True
See program code
Background Concept
Enqueue() is the queue operation that inserts a new item at the tail end of the queue. In a linear array-based queue:
QueueHeadpoints to the first stored itemQueueTailpoints to the last stored item- insertion happens at the tail
Because this is a linear queue, once QueueTail reaches the last valid array index, the queue is considered full. With 20 elements, the valid indices are 0 to 19, so full means QueueTail == 19.
There is also a special case when the queue is empty. If both pointers are -1, the first insertion must set up the queue so that head points at the first item.
Understanding the Question
This part asks for a function called Enqueue() that:
- takes the data item as a parameter
- inserts it if the queue is not full
- updates the correct pointer values
- returns
Trueif insertion happened - returns
Falseif the queue is full
The important detail is that the queue is linear, not circular, so the full check is simply whether the tail has reached index 19.
Approach
The function should work in this order:
- Check whether the queue is full.
- If full, return
Falseimmediately. - If the queue is empty, set
QueueHeadto0. - Move
QueueTailto the next free position. - Store the new item there.
- Return
True.
In Python, because the queue variables are global and are updated inside the function, they must be declared with global.
Step-by-Step Reasoning
def Enqueue(DataToAdd):
- defines the function with one parameter, the item to be inserted
global QueueData, QueueHead, QueueTail
- allows the function to modify the shared queue structure and pointers
if QueueTail == 19:
- checks whether the last valid array index is already occupied
- if yes, there is no more space in this linear queue
return False
- signals that insertion failed because the queue is full
if QueueHead == -1:
- checks whether the queue is empty
- when empty, this insertion will create the first item in the queue
QueueHead = 0
- the first item must be at index
0
QueueTail += 1
- moves the tail to the next free position
- when the queue was empty, tail changes from
-1to0 - otherwise it moves from the current last item to the next index
QueueData[QueueTail] = DataToAdd
- stores the new item at the tail position
return True
- signals that insertion succeeded
This logic correctly handles both ordinary insertions and the first insertion into an empty queue.
Key Takeaways
- In a queue, insertion always happens at the tail.
- A linear queue is full when the tail reaches the last array index.
- Empty queues often need a special case for the first insertion.
- Returning a Boolean makes it easy for the calling code to react to success or failure.
Common Mistakes
- Checking the wrong full condition, such as
QueueHead == 19instead ofQueueTail == 19. - Forgetting to set
QueueHeadwhen inserting into an empty queue. - Storing the item before moving the tail, which can write to index
-1in the empty case. - Returning the strings
"True"or"False"instead of the Python Boolean valuesTrueandFalse.
Things to Be Careful About
- The final valid index is
19, not20. - This is a linear queue, so no wrap-around logic is used.
- In Python,
globalis needed because the function changes the pointer variables. - The function must return immediately when full, otherwise it might still try to insert.
The function Dequeue() returns "false" if the queue is empty. If the queue is not empty, it returns the next item in the queue and updates the appropriate pointer(s).
Write program code for Dequeue().
Save your program.
Copy and paste the program code into part 3(c) in the evidence document.
Answer
def Dequeue():
global QueueData, QueueHead, QueueTail
if QueueHead == -1:
return "false"
Item = QueueData[QueueHead]
if QueueHead == QueueTail:
QueueHead = -1
QueueTail = -1
else:
QueueHead += 1
return Item
See program code
Background Concept
Dequeue() is the queue operation that removes and returns the item at the front of the queue. In a first-in, first-out structure, that means removing the item at QueueHead.
There are three main cases to handle:
- the queue is empty
- the queue has exactly one item
- the queue has more than one item
An empty queue cannot return a stored value, so this question specifies a sentinel return value of "false".
If the queue has only one item, removing it makes the queue empty again, so both pointers must return to -1.
Understanding the Question
This part asks for a function that:
- returns
"false"if the queue is empty - otherwise returns the next item from the front
- updates the queue pointers correctly after removal
The phrase "next item" tells you the queue must remove from the head, not the tail.
Approach
The logic should be:
- Check whether the queue is empty using
QueueHead. - If empty, return
"false". - Save the current head item in a variable.
- Decide whether this is the last item.
- If it is the last item, reset both pointers to
-1. - Otherwise, move
QueueHeadon by one. - Return the saved item.
Saving the item before moving the pointer is essential, otherwise the value could be lost.
Step-by-Step Reasoning
def Dequeue():
- defines the function with no parameter because it removes whatever is already at the front
global QueueData, QueueHead, QueueTail
- needed because the function updates the shared queue pointers
if QueueHead == -1:
QueueHead == -1means the queue is empty
return "false"
- returns the exact sentinel value required by the question
Item = QueueData[QueueHead]
- stores the front item before changing any pointer values
if QueueHead == QueueTail:
- if head and tail point to the same index, there is only one item in the queue
QueueHead = -1
QueueTail = -1
- after removing that only item, the queue becomes empty again
else:
- this means there were at least two items
QueueHead += 1
- moves the head pointer to the next item in the queue
return Item
- returns the value that was removed
That is the correct FIFO behaviour.
Key Takeaways
- Queue removal always happens at the head.
- Empty, single-item and multi-item queues must be handled separately.
- Save the item before changing the pointers.
- Sentinel return values are often used when no real item can be returned.
Common Mistakes
- Removing from
QueueTailinstead ofQueueHead, which would turn the queue into stack-like behaviour. - Returning Python
Falseinstead of the required string"false". - Forgetting to reset both pointers when the last item is removed.
- Incrementing
QueueHeadbefore saving the item, which returns the wrong value.
Things to Be Careful About
- The exact required empty return value is
"false"in lowercase. QueueHead == QueueTailmeans one item remains, not that the queue is full.- If you reset only one pointer when the last item is removed, the queue state becomes inconsistent.
The string values to be stored in the queue are 7 characters long. The first 6 characters are digits and the 7th character is a check digit. The check digit is calculated from the first 6 digits using this algorithm:
- multiply the digits in position 0, position 2 and position 4 by 1
- multiply the digits in position 1, position 3 and position 5 by 3
- calculate the sum of the products (add together the results from all of the multiplications)
- divide the sum of the products by 10 and round the result down to the nearest integer to get the check digit
- if the check digit equals 10 then it is replaced with 'X'.
Example:
Data is 954123
| Character position | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| Digit | 9 | 5 | 4 | 1 | 2 | 3 |
| Multiplier | 1 | 3 | 1 | 3 | 1 | 3 |
| Product | 9 | 15 | 4 | 3 | 2 | 9 |
Sum of products = 9 + 15 + 4 + 3 + 2 + 9 = 42
Divide sum of products by 10: 42 / 10 = 4 (rounded down)
The check digit = 4. This is inserted into character position 6.
The data including the check digit is: 9541234
A 7-character string is valid if the 7th character matches the check digit for that data. For example, the data 9541235 is invalid because the 7th character (5) does not match the check digit for 954123.
The subroutine StoreItems() takes ten 7-character strings as input from the user and uses the check digit to validate each input.
Each valid input has the check digit removed and is stored in the queue using Enqueue().
An appropriate message is output if the item is inserted. An appropriate message is output if the queue is already full.
Invalid inputs are not stored in the queue.
The subroutine counts and outputs the number of invalid items that were entered.
StoreItems() can be a procedure or a function as appropriate.
Write program code for StoreItems().
Save your program.
Copy and paste the program code into part 3(d)(i) in the evidence document.
Answer
def StoreItems():
Invalid = 0
for Count in range(10):
Item = input("Enter data")
Total = 0
for Index in range(6):
Digit = int(Item[Index])
if Index % 2 == 0:
Total += Digit
else:
Total += Digit * 3
CheckDigit = Total // 10
if CheckDigit == 10:
CheckCharacter = "X"
else:
CheckCharacter = str(CheckDigit)
if Item[6] == CheckCharacter:
if Enqueue(Item[:6]):
print("Inserted item")
else:
print("Queue is full")
else:
Invalid += 1
print("There were", Invalid, "Invalid items")
See program code
Background Concept
This part combines two ideas:
- validating data using a check digit
- storing valid data in a queue
A check digit is an extra character added to data so that the program can test whether the data is likely to be correct. Here, the first 6 characters are digits and the 7th character is the check digit.
The algorithm is positional and weighted:
- positions
0,2,4are multiplied by1 - positions
1,3,5are multiplied by3 - the products are added
- the total is divided by
10 - the integer part becomes the check digit
- if that value is
10, the character becomesX
Only if the entered 7th character matches this calculated character is the item valid.
If valid, only the first 6 characters are stored in the queue, not the full 7-character string.
Understanding the Question
The subroutine must do all of the following:
- read exactly 10 inputs from the user
- calculate the correct check digit for each input
- compare it with the entered 7th character
- if valid, remove the check digit and try to store the first 6 characters using
Enqueue() - output a message if inserted
- output a message if the queue is already full
- not store invalid items
- count how many invalid items were entered
- output that count at the end
Notice the difference between invalid data and a full queue:
- invalid data fails the check-digit test
- a full queue means the data may be valid, but there is no space left to store it
Those are separate cases.
Approach
A good structure is:
- Set an
Invalidcounter to0. - Repeat 10 times.
- Read a 7-character string.
- Process the first 6 characters one by one.
- Multiply by
1or3depending on whether the index is even or odd. - Use integer division by
10to get the check value. - Convert
10toX, otherwise convert the number to a string. - Compare with character position
6. - If valid, call
Enqueue()with the first 6 characters. - If invalid, increment the counter.
- At the end, print the number of invalid items.
Using Index % 2 is a neat way to decide whether the position is even or odd.
Step-by-Step Reasoning
Invalid = 0
- starts the count of invalid inputs
for Count in range(10):
- repeats exactly 10 times, because the question says ten inputs are taken
Item = input("Enter data")
- reads one 7-character string
Total = 0
- initialises the running total for the weighted products
for Index in range(6):
- processes positions
0to5, which are the six data digits
Digit = int(Item[Index])
- converts the current character into a number so arithmetic can be performed
if Index % 2 == 0:
- even positions are
0,2,4
Total += Digit
- multiplier
1is implied
else:
- odd positions are
1,3,5
Total += Digit * 3
- applies multiplier
3
CheckDigit = Total // 10
//is integer division in Python, so it discards any remainder- that matches the instruction to round down
if CheckDigit == 10:
CheckCharacter = "X"
else:
CheckCharacter = str(CheckDigit)
- converts the calculated value into the character that should appear in position
6
if Item[6] == CheckCharacter:
- checks whether the input is valid
if Enqueue(Item[:6]):
- only the first 6 characters are stored
Item[:6]means characters at positions0to5
print("Inserted item")
- printed when the valid data is successfully stored
print("Queue is full")
- printed if the data is valid but there is no room in the queue
else: Invalid += 1
- invalid inputs are counted and not stored
Finally:
print("There were", Invalid, "Invalid items")
- outputs the total number of invalid entries after all 10 inputs have been processed
Key Takeaways
- Check-digit validation is a systematic way to test entered data.
- Positional weighting often depends on even and odd index values.
- Integer division is useful when the algorithm says to round down.
- Validation and storage are separate tasks: validate first, then enqueue.
Common Mistakes
- Including the 7th character in the checksum calculation instead of only the first 6 digits.
- Using normal division
/without converting to an integer, which gives a real number. - Forgetting the special case where check digit
10becomesX. - Storing the full 7-character string instead of removing the check digit first.
- Counting full-queue cases as invalid inputs, which they are not.
Things to Be Careful About
- Python string indexing starts at
0, so the check character isItem[6]. range(6)processes positions0to5;range(7)would be wrong here.- The queue stores strings, so the calculated check digit must be converted to a string before comparison.
- The queue-full message is only for valid items that cannot be inserted because the queue has no space.
Write program code to amend the main program to:
- call
StoreItems() - call
Dequeue() - output a suitable message if the queue was empty
- output the returned value if the queue was not empty.
Save your program.
Copy and paste the program code into part 3(d)(ii) in the evidence document.
Answer
StoreItems()
ItemCode = Dequeue()
if ItemCode == "false":
print("Queue is empty")
else:
print("Item code", ItemCode)
See program code
Background Concept
The main program coordinates the smaller subroutines. Here, the main program must:
- fill the queue by calling
StoreItems() - remove one item by calling
Dequeue() - react appropriately depending on whether a real item was returned
Because Dequeue() may return either a stored item or the sentinel string "false", the main program must test that return value before deciding what to print.
Understanding the Question
This part does not ask you to rewrite the whole program. It only asks you to amend the main program so that it:
- calls
StoreItems() - calls
Dequeue() - outputs a message if the queue is empty
- otherwise outputs the returned value
The important clue is that Dequeue() returns "false" when empty, so the main program must compare against that exact value.
Approach
Use a simple sequence:
- Call
StoreItems(). - Save the result of
Dequeue()in a variable. - Use an
ifstatement. - If the result is
"false", print an empty-queue message. - Otherwise print the item code.
Saving the return value first makes the test and the output easy.
Step-by-Step Reasoning
StoreItems()
- runs the input and validation routine
- after this call, all valid items that fitted in the queue have been enqueued
ItemCode = Dequeue()
- removes the next item from the queue, if there is one
- stores the returned value so it can be tested and printed
if ItemCode == "false":
- checks the sentinel value returned by
Dequeue()when the queue is empty
print("Queue is empty")
- prints a suitable message for that case
else:
- this means a real item was returned
print("Item code", ItemCode)
- prints the dequeued value
That exactly matches the required behaviour.
Key Takeaways
- The main program often just links together previously written subroutines.
- A function's return value should usually be stored in a variable before testing it.
- Sentinel values let the caller detect special conditions such as an empty queue.
Common Mistakes
- Calling
Dequeue()without storing the returned value, then having nothing to test or print. - Comparing with Python
Falseinstead of the required string"false". - Printing the item even when the queue was empty.
- Forgetting to call
StoreItems()beforeDequeue().
Things to Be Careful About
- The required empty value is the string
"false", not a Boolean. - The message can be any suitable wording, but it must clearly show the empty-queue case.
StoreItems()must be called beforeDequeue(), otherwise the queue will still be empty unless something else has already inserted data.
Test the program with the following inputs in the order given:
999999X
1251484
5500212
0033585
9845788
6666666
3258746
8111022
7568557
0012353
Take a screenshot of the output(s).
Save your program.
Copy and paste the screenshot into part 3(d)(iii) in the evidence document.
Answer
For the ten inputs in the order given, the console output is:
Enter data999999X
Inserted item
Enter data1251484
Inserted item
Enter data5500212
Inserted item
Enter data0033585
Enter data9845788
Inserted item
Enter data6666666
Enter data3258746
Enter data8111022
Inserted item
Enter data7568557
Inserted item
Enter data0012353
There were 4 Invalid items
Item code 999999
See expected console output
Background Concept
A test run of a queue program is really a trace of two linked processes:
- input validation using the check-digit algorithm
- queue behaviour using FIFO order
Each 7-character input is first validated. If valid, the first 6 characters are enqueued. After all ten inputs, the program prints the number of invalid items, then Dequeue() removes the first valid item that was inserted.
Because queues are first-in, first-out, the earliest valid item entered will be the one returned at the end.
Understanding the Question
This part gives a fixed set of ten inputs and asks for the output that would appear on screen.
So you must work through:
- which inputs are valid
- which ones print
Inserted item - how many are invalid
- what value
Dequeue()returns afterStoreItems()finishes
The screenshot in the marking material confirms that no individual message is printed for an invalid item; the program just moves on to the next input.
Approach
For each input:
- use the first 6 digits to calculate the check character
- compare it with the 7th character
- if they match, the first 6 digits are stored in the queue and
Inserted itemis printed - if they do not match, the item is counted as invalid
At the end:
- output the invalid total
- remove and output the first queued item
A small table is the easiest way to keep track.
Step-by-Step Reasoning
Here is the validation trace:
| Input | First 6 digits | Calculated check character | Valid? | Stored value |
|---|---|---|---|---|
999999X | 999999 | X | Yes | 999999 |
1251484 | 125148 | 4 | Yes | 125148 |
5500212 | 550021 | 2 | Yes | 550021 |
0033585 | 003358 | 4 | No | - |
9845788 | 984578 | 8 | Yes | 984578 |
6666666 | 666666 | 7 | No | - |
3258746 | 325874 | 5 | No | - |
8111022 | 811102 | 2 | Yes | 811102 |
7568557 | 756855 | 7 | Yes | 756855 |
0012353 | 001235 | 2 | No | - |
So the valid items are enqueued in this order:
999999125148550021984578811102756855
There are 4 invalid items:
0033585666666632587460012353
After StoreItems() finishes, the program prints:
There were 4 Invalid items
Then Dequeue() removes the first queued item. Because the queue is FIFO, that is 999999.
So the final output line is:
Item code 999999
That gives the complete console output shown in the answer.
Key Takeaways
- When tracing a program like this, separate the validation stage from the queue stage.
- FIFO means the first valid inserted item is the first one removed.
- A final output line often depends on all earlier queue operations, not just the last input.
Common Mistakes
- Treating
999999Xas invalid because the check character is a letter. In fact,10must be replaced byX, so it is valid. - Storing the full 7-character input instead of just the first 6 characters.
- Counting only failed insertions and forgetting to count invalid inputs.
- Returning the most recent valid item instead of the first valid item when tracing
Dequeue().
Things to Be Careful About
- The prompt and the user's typed input appear on the same line because
input("Enter data")displays the prompt before the user types. - No queue-full message appears in this test because only 6 valid items are inserted into a queue of size 20.
- The case of
Invalidin the final printed message should match the program output shown here.