Computer Science 9618/43 — 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
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.
One source file is used to answer Question 2. The file is called Trees.txt
1 A program needs to take integer numbers as input, sort the numbers and then search for a specific number.
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] * 20
NumberItems = 0
See program code
Background Concept
In Paper 4, you write real program code in a high-level language. Here, the question wants storage for up to 20 integers and a separate variable that records how many of those positions are actually in use. That is an important distinction: the list may have capacity for 20 items, but the program may currently contain fewer than 20 real values.
In Python, there is no separate array declaration like in some other languages, so a list is used instead. A common way to represent "space for 20 integers" is to create a list of length 20 filled with placeholder values such as 0.
Understanding the Question
You are asked only to declare:
DataStored, the global 1D array/list with room for 20 integersNumberItems, the global variable that stores how many numbers are currently held
Nothing is being input or processed yet. This part is only setting up the global data items used by the later procedures and functions.
Approach
Use a Python list of length 20 for DataStored, then create NumberItems as an integer variable. Since Python declares variables by assigning a value, initialising NumberItems to 0 is the normal way to do this.
Step-by-Step Reasoning
DataStored = [0] * 20
[0]is a one-item list containing0- multiplying by
20creates a list of twenty zeroes - this gives a fixed block of storage the rest of the program can use
NumberItems = 0
- this creates the variable
0is a sensible starting value because no user data has been entered yet- later parts of the program will update it after validated input
This matches the question exactly: one global structure to hold values and one global count of how many values are currently stored.
Key Takeaways
- A list can be used in Python where the question describes an array.
- The storage capacity and the number of items currently used are not the same thing.
- Initialising shared global data correctly makes later procedures much simpler.
Common Mistakes
- Creating an empty list instead of space for 20 items. The question specifically says there must be room for up to 20 integers.
- Forgetting
NumberItems. Later parts depend on it to know how many values are valid. - Using 20 as the current number of items. That would be wrong before any data is entered.
Things to Be Careful About
- Keep the identifier names exactly as given:
DataStoredandNumberItems. - The list length should be 20, not 19 or 21.
NumberItemsis the count of valid entries, so later output and searching should use only the firstNumberItemspositions.
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
An initialisation routine is often used to set up the main data before any later processing such as sorting or searching. This question also involves input validation. Validation means checking that the input meets the rules required by the program. Here, the number of items must be between 1 and 20 inclusive.
A good validation pattern is:
- read a value
- test whether it is valid
- if invalid, keep asking until it becomes valid
After the quantity is known, a count-controlled loop is used to read exactly that many numbers into the array/list.
Understanding the Question
The procedure Initialise() must do three jobs:
- ask how many numbers the user wants to enter
- reject the value if it is not in the range 1 to 20
- read that many integers and store them in
DataStored
The question says DataStored and NumberItems are global, so the procedure must work with those same shared variables rather than making local replacements.
Approach
First read NumberItems. Then use a while loop to repeat the prompt while the value is outside the allowed range. Once the quantity is valid, use a for loop from 0 up to NumberItems - 1 and store each entered integer into the corresponding position of DataStored.
Step-by-Step Reasoning
global DataStored, NumberItems
- this makes it clear the procedure is using the global list and count variable
- without this, assigning to
NumberItemsinside the procedure would create a separate local variable in Python
NumberItems = int(input(...))
- reads the quantity from the user
int(...)converts the keyboard input from a string to an integer
while NumberItems < 1 or NumberItems > 20:
- this is the validation condition
- values below 1 are invalid because at least one number must be entered
- values above 20 are invalid because the list only has room for 20 items
- using
oris correct because either kind of invalid value must be rejected
Inside the loop, the quantity is requested again until it becomes valid.
for Count in range(NumberItems):
- if
NumberItemsis 5,range(5)gives indexes0, 1, 2, 3, 4 - that matches the first five valid positions in a Python list
DataStored[Count] = int(input("Enter number"))
- each number is read and stored directly into the next free slot
- only the first
NumberItemspositions become meaningful data
Key Takeaways
- Validation loops keep asking until input satisfies the required rule.
- Inclusive bounds mean both 1 and 20 are allowed.
- The count variable controls how many values are stored.
- In Python, list positions are zero-based, so the first item is at index 0.
Common Mistakes
- Using
andinstead oforin the validation condition. No number can be both less than 1 and greater than 20 at the same time, soandwould fail. - Looping 20 times instead of
NumberItemstimes. That would ask for too many numbers. - Storing the values starting at index 1 in Python. Python lists start at 0.
- Forgetting to convert the input to
int, leaving values as strings.
Things to Be Careful About
- The valid range is inclusive: 1 and 20 must both be accepted.
- Use the exact variable names from the question.
- Only
NumberItemspositions should be treated as real data later, even though the list has length 20. - This question does not ask for validation of each individual number, only the quantity of numbers.
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[:NumberItems])
See program code
Background Concept
The main program controls the overall sequence of execution. In structured programming, the main program usually performs high-level steps and calls procedures to handle detail. Here, Initialise() is responsible for gathering and storing the data, so the main program only needs to prepare the variables, call that procedure and display the stored values.
When a list has spare capacity, it is important to output only the valid part. In this question, DataStored has room for 20 values, but only the first NumberItems positions contain meaningful user input.
Understanding the Question
This part tells you exactly what the main program should do:
- set
NumberItemsto 0 - call
Initialise() - output the contents of
DataStored
Because the list has unused positions, printing the entire 20-item list would show extra zeroes that are not really part of the user data. So the output should be limited to the used section.
Approach
Assign 0 to NumberItems, call Initialise() so that the user can enter the data, then print a slice of the list containing only the first NumberItems values.
Step-by-Step Reasoning
NumberItems = 0
- this gives the count variable a known starting value
- although
Initialise()will change it, initialising it is part of the required main program
Initialise()
- this calls the procedure written in part (b)
- that procedure validates the quantity and stores the numbers in
DataStored - after the call finishes,
NumberItemsholds the number of values entered
print(DataStored[:NumberItems])
DataStored[:NumberItems]means "from the start of the list up to, but not including, indexNumberItems"- if
NumberItemsis 5, this prints the first five stored values - it avoids printing the unused remainder of the 20-element list
Key Takeaways
- The main program should coordinate tasks, not duplicate the work of procedures.
- Slicing is a clean way in Python to output only the valid part of a list.
- A count variable such as
NumberItemsis essential when the list capacity is larger than the current data set.
Common Mistakes
- Printing the whole list, which would show unused values such as extra zeroes.
- Forgetting to call
Initialise(), leaving the list unfilled. - Not resetting
NumberItemsas the question explicitly requires.
Things to Be Careful About
- Use
DataStored[:NumberItems], notDataStored[NumberItems]. The latter refers to just one item. - The output happens after
Initialise(), not before it. - Keep the call name exactly as
Initialise().
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
Using inputs 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
A test run checks whether the code behaves as expected for given input. In this question, the most important feature being tested is validation: the first quantity entered is invalid, so the program should reject it and ask again.
Once a valid quantity is accepted, the program stores exactly that many values and then outputs the populated part of the list.
Understanding the Question
You are not being asked to write new code here. You are being asked to run the program using a specific sequence of inputs:
30539412
The first value is the quantity of numbers. Since 30 is outside the valid range 1 to 20, the program must prompt again. The second quantity, 5, is valid, so the next five values become the data stored in the list.
Approach
Simulate the program in order:
- enter
30and fail validation - enter
5and pass validation - store the next five numbers into
DataStored[0]toDataStored[4] - print the first five values
Step-by-Step Reasoning
First prompt:
- user enters
30 - the validation test checks whether the quantity is less than 1 or greater than 20
30 > 20, so it is invalid- the program asks again
Second prompt:
- user enters
5 5is within the valid range, so input continues
Now the loop runs 5 times:
- first number
3goes intoDataStored[0] - second number
9goes intoDataStored[1] - third number
4goes intoDataStored[2] - fourth number
1goes intoDataStored[3] - fifth number
2goes intoDataStored[4]
NumberItems is 5, so print(DataStored[:NumberItems]) prints the first five entries only:
[3, 9, 4, 1, 2]
That is why the final console output shows the repeated quantity prompt followed by the unsorted list.
Key Takeaways
- Test data should include invalid input when validation is present.
- A dry run is often enough to predict a screenshot output exactly.
- The list is unsorted at this stage because no sorting procedure has been called yet.
Common Mistakes
- Forgetting that
30is rejected and therefore missing the repeated prompt. - Sorting the list in this part. Sorting has not happened yet.
- Showing all 20 list positions instead of just the first 5 valid values.
Things to Be Careful About
- Keep the inputs in the exact order given.
- Only the quantity is validated here, not each of the five stored numbers.
- The final displayed list must remain in input order:
3, 9, 4, 1, 2.
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 Pass in range(NumberItems - 1):
for Count in range(NumberItems - Pass - 1):
if DataStored[Count] > DataStored[Count + 1]:
Temp = DataStored[Count]
DataStored[Count] = DataStored[Count + 1]
DataStored[Count + 1] = Temp
See program code
Background Concept
Bubble sort is a simple comparison-based sorting algorithm. It repeatedly scans through a list, compares adjacent items, and swaps them if they are in the wrong order. After one full pass, the largest unsorted value has "bubbled" to the end. Repeating this process eventually produces the whole list in ascending order.
The usual implementation uses nested loops:
- outer loop for each pass
- inner loop for adjacent comparisons within that pass
Understanding the Question
You must write a procedure BubbleSort() that sorts the values already stored in DataStored into ascending numerical order. The key details are:
- the sort must use bubble sort
- it must work only on the used part of the array/list
- the number of used items is stored in
NumberItems
Approach
Use two loops. The outer loop counts the passes. The inner loop compares each pair DataStored[Count] and DataStored[Count + 1]. If the left value is larger, swap them. Because the largest item settles at the end after each pass, the inner loop can be shortened by Pass positions each time.
Step-by-Step Reasoning
global DataStored, NumberItems
- the procedure sorts the shared list used by the rest of the program
for Pass in range(NumberItems - 1):
- if there are
nitems, bubble sort needs at mostn - 1passes - for 5 items, this gives 4 passes
for Count in range(NumberItems - Pass - 1):
- this controls adjacent comparisons within the current pass
- the
- 1is needed because each comparison usesCountandCount + 1 - the
- Passavoids rechecking values already bubbled into their final places
if DataStored[Count] > DataStored[Count + 1]:
- if the left item is larger than the right item, they are out of ascending order
- therefore they must be swapped
Swap using Temp:
- save the first value in
Temp - move the second value left
- copy
Tempinto the second position
This sorts the list in place, meaning the actual DataStored list is rearranged rather than creating a new list.
Key Takeaways
- Bubble sort works by repeated adjacent comparisons and swaps.
- Nested loops are the standard structure for coding bubble sort.
- The inner loop usually gets shorter each pass because the largest items are already settled at the end.
Common Mistakes
- Using the wrong comparison direction, which would sort descending instead of ascending.
- Letting the inner loop run too far and then trying to access
Count + 1beyond the list section in use. - Forgetting the temporary variable and overwriting one of the values during the swap.
Things to Be Careful About
- Only sort the first
NumberItemsentries, not all 20 positions. - The inner loop limit must allow safe access to
Count + 1. - The procedure changes
DataStoreddirectly, so no return value is needed.
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[:NumberItems])
See program code
Background Concept
Once a sorting procedure has been written, the main program must call it at the correct point in the sequence. Sorting can only happen after the data has been input, because there is nothing to sort beforehand.
After sorting, the displayed output should reflect the new ordered arrangement.
Understanding the Question
This part does not ask you to rewrite the entire program from scratch. It asks you to amend the main program so that after Initialise() fills the list, BubbleSort() is called and then the contents of DataStored are output.
Approach
Keep the same general structure as before:
- initialise
NumberItems - call
Initialise() - call
BubbleSort() - print the used section of the list
Step-by-Step Reasoning
NumberItems = 0
- gives the counter a starting value
Initialise()
- reads and stores the user values
- updates
NumberItems
BubbleSort()
- rearranges the first
NumberItemsitems into ascending order - it works in place, so the actual list is changed
print(DataStored[:NumberItems])
- prints the sorted values only
- avoids showing unused list positions beyond the entered data
The key amendment compared with part (c) is the added call to BubbleSort() before the output.
Key Takeaways
- The order of procedure calls matters.
- Sorting is done after input and before the final output.
- Reusing procedures makes the main program short and clear.
Common Mistakes
- Calling
BubbleSort()beforeInitialise(), which would sort placeholder values instead of real input. - Printing before sorting, which would still show the unsorted list.
- Outputting all 20 positions instead of just the first
NumberItemsitems.
Things to Be Careful About
- This is an amendment to the main program, so the sort call must be inserted in the correct place.
- Keep the output as the active slice of the list.
- The procedure name must match exactly:
BubbleSort().
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
Using inputs 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 a sorting routine means comparing the original input order with the final sorted order. Bubble sort repeatedly swaps adjacent out-of-order items until the whole list is in ascending order.
Because the list is printed after sorting, the console output should show the numbers from smallest to largest rather than in their original entry sequence.
Understanding the Question
The inputs are:
- quantity:
5 - numbers:
3, 9, 4, 1, 2
There is no invalid quantity here, so the program accepts 5 immediately, stores the five numbers, sorts them and outputs the sorted list.
Approach
First record the entered numbers in the order stored. Then mentally apply the bubble sort and write the final console display.
Step-by-Step Reasoning
After input, the used section of DataStored is:
[3, 9, 4, 1, 2]
Now bubble sort runs:
- compare
3and9→ already correct - compare
9and4→ swap - compare
9and1→ swap - compare
9and2→ swap
After the first pass,9has moved to the end.
Continue with the remaining unsorted section:
3, 4, 1, 2becomes ordered through further adjacent swaps- eventually the full list becomes
[1, 2, 3, 4, 9]
That is the list printed by print(DataStored[:NumberItems]).
Key Takeaways
- A sorting test should clearly show the change from original order to ascending order.
- Bubble sort is easy to trace because it only compares adjacent pairs.
- The final output should contain the same values, just rearranged.
Common Mistakes
- Writing the original unsorted list instead of the sorted one.
- Missing one value during sorting and producing an incomplete output.
- Sorting descending instead of ascending.
Things to Be Careful About
- Do not include extra zeroes from the unused part of the 20-element list.
- The input quantity is valid first time, so there is only one quantity prompt.
- The final order must be exactly
[1, 2, 3, 4, 9].
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
Low = 0
High = NumberItems - 1
while Low <= High:
Mid = (Low + High) // 2
if DataStored[Mid] == DataToFind:
return Mid
elif DataStored[Mid] < DataToFind:
Low = Mid + 1
else:
High = Mid - 1
return -1
See program code
Background Concept
Binary search is an efficient searching algorithm for sorted data. It works by repeatedly looking at the middle item of the current search range and deciding whether the target must be to the left or to the right. Each comparison cuts the remaining search space roughly in half.
Important rules:
- the data must already be sorted
- the algorithm keeps two bounds, usually called
LowandHigh - if the target is smaller than the middle value, search the lower half
- if the target is larger, search the upper half
- if the bounds cross, the item is not present
Understanding the Question
You must write an iterative function BinarySearch() that:
- takes
DataToFindas a parameter - searches the array/list
DataStored - returns the index if found
- returns
-1if not found
Because this is binary search, the array is assumed to have been sorted first by BubbleSort().
Approach
Set Low to the first used index and High to the last used index. While the search range is still valid, find the middle index, compare its value with DataToFind, and adjust the appropriate bound. If a match occurs, return the middle index immediately. If the loop ends with no match, return -1.
Step-by-Step Reasoning
Low = 0
- the first valid Python list index is 0
High = NumberItems - 1
- if there are 5 items, the last used index is 4
- this ensures only the populated part of the list is searched
while Low <= High:
- as long as the lower bound has not passed the upper bound, there is still a range to search
- once
Lowbecomes greater thanHigh, the item cannot be present
Mid = (Low + High) // 2
//performs integer division in Python- binary search needs an integer index, not a decimal value
if DataStored[Mid] == DataToFind:
- exact match found, so return
Mid
elif DataStored[Mid] < DataToFind:
- the target must be in the upper half if it exists
- set
Low = Mid + 1
else:
- the middle value is greater than the target
- the target must be in the lower half if it exists
- set
High = Mid - 1
return -1
- if the loop finishes, every possible position has been ruled out
- the question specifically requires
-1when the value is not found
Key Takeaways
- Binary search is only suitable for sorted data.
- The algorithm is efficient because it repeatedly halves the search range.
- Correct bound updates are the heart of binary search.
- Returning
-1is a standard way to signal "not found".
Common Mistakes
- Trying to use binary search on unsorted data.
- Setting
High = NumberItemsinstead ofNumberItems - 1, which searches one position too far. - Forgetting integer division for the middle index.
- Updating the wrong bound after a comparison, which can cause wrong answers or infinite loops.
- Returning the value found instead of the index found.
Things to Be Careful About
- Python indexes start at 0, so the first element has index 0.
- The loop condition must be
Low <= High, not just<, otherwise a one-item range could be skipped. - Use the exact required return for failure:
-1. - This function reads global data, so it should search only the first
NumberItemsentries.
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[:NumberItems])
DataToFind = int(input("Enter a number to find"))
print(BinarySearch(DataToFind))
See program code
Background Concept
A function call is used when a section of code computes and returns a value. In this question, BinarySearch() returns either the index of the found item or -1. The main program must therefore gather the search key, pass it into the function and then display the returned result.
Because binary search requires sorted data, the main program must still sort the list before asking for the item to find.
Understanding the Question
You need to amend the main program so that it now:
- takes a number as input from the user
- calls
BinarySearch()with that number - outputs the value returned
From the earlier parts, the program must still initialise the data and sort it first. The screenshot in the mark scheme also shows the sorted list being displayed before the search prompt.
Approach
Keep the earlier main-program structure, then add:
- an input statement to get the target value
- a call to
BinarySearch(DataToFind) - an output of the returned index or
-1
Step-by-Step Reasoning
NumberItems = 0
- initialise the counter
Initialise()
- read and store the numbers
BubbleSort()
- put the data into ascending order so binary search will work correctly
print(DataStored[:NumberItems])
- display the sorted list
- this matches the progression shown in the test screenshot
DataToFind = int(input("Enter a number to find"))
- asks the user for the search target
- converts the typed value into an integer
print(BinarySearch(DataToFind))
- passes the search target into the function
- displays the returned index directly
- if the item is missing, the printed result is
-1
Key Takeaways
- Functions return values to the main program.
- Binary search must come after sorting, not before it.
- The main program should pass the user input into the function exactly as required.
Common Mistakes
- Forgetting to sort before calling
BinarySearch(). - Reading the target as a string instead of an integer.
- Calling
BinarySearch()but not printing its return value. - Printing the unsorted list or omitting the list output when the expected test run shows it.
Things to Be Careful About
- The search input must happen after the sorted list exists.
- Use the exact parameter name in the call if you store it as
DataToFind. - The displayed result is the index, not the value itself.
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
Using inputs 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 find2
1
Using inputs 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 find7
-1
See expected console output
Background Concept
A complete search test should include both:
- a value that is present in the data
- a value that is absent from the data
This proves that the function works in both success and failure cases. Since the search is binary search, the data is first sorted, then the search function examines the middle of the current range and narrows the search repeatedly.
Understanding the Question
There are two required test runs.
Test 1 inputs:
- quantity
5 - data
1, 6, 2, 8, 10 - search target
2
Test 2 uses the same five data values but changes the search target to 7.
You need the resulting console output for both.
Approach
For each test:
- store the five values
- sort them into ascending order
- show the sorted list
- run binary search on the target
- output the returned index or
-1
Step-by-Step Reasoning
First, in both tests, the entered data is:
[1, 6, 2, 8, 10]
After BubbleSort(), the list becomes:
[1, 2, 6, 8, 10]
Test 1: search for 2
Indexes are:
- index 0 → 1
- index 1 → 2
- index 2 → 6
- index 3 → 8
- index 4 → 10
Binary search checks the middle first:
- middle index is 2, value is 6
2is smaller than 6, so search the left half- new range is index 0 to 1
- middle becomes index 0, value 1
2is larger than 1, so search the upper half of that range- new range is index 1 to 1
- middle is index 1, value 2
- match found, so return
1
Test 2: search for 7
Again start with middle index 2, value 6:
7is larger than 6, so search right half- new range is index 3 to 4
- middle becomes index 3, value 8
7is smaller than 8, so search left of that- new range becomes index 3 to 2
- now
Low > High, so the search stops - return
-1
That is why the first test prints 1 and the second prints -1.
Key Takeaways
- Good testing includes both found and not-found cases.
- Binary search returns an index, not just a yes/no answer.
- Tracing the changing
Low,HighandMidvalues explains the result clearly.
Common Mistakes
- Forgetting to sort before searching.
- Returning
2for Test 1 because the value is 2, instead of returning its index1. - Using one-based positions instead of Python's zero-based indexes.
- Forgetting that the not-found case must return
-1.
Things to Be Careful About
- The sorted list should be shown before the search prompt, matching the developed main program.
- The five data values are the same in both tests; only the final search value changes.
- Python indexing starts at 0, so the value
2is at index1, not position2.
2 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.
| 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:
# self.__TreeName: str
# self.__HeightGrowth: int
# self.__MaxHeight: int
# self.__MaxWidth: int
# self.__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 blueprint for creating objects. Each object stores its own data in attributes and can provide methods to work with that data. A constructor is the special routine that runs when an object is created. Its job is usually to receive starting values and store them in the object's attributes.
This question also requires encapsulation. Encapsulation means the internal data of the object should not be accessed directly from outside the class. In Python, private attributes are typically shown using a double underscore prefix such as __TreeName. That does not make them absolutely inaccessible, but it is the normal exam-standard way to show that they are private.
Understanding the Question
You are told that the program uses a class called Tree to store five pieces of data:
- tree name
- yearly height growth
- maximum height
- maximum width
- whether it is evergreen
The task only asks for the class declaration and the constructor. It explicitly says not to declare the other methods yet. Because this is Python, the appropriate constructor is __init__. The question also says that, in Python, attribute declarations should be included using comments, so those comments should appear in the class.
Approach
The simplest correct approach is:
- Declare the class
Tree. - Add comment lines showing the private attributes.
- Write
__init__with one parameter for each attribute value. - Copy each parameter into the matching private attribute using
self.
That is enough for this part. No getters should be included here because the question tells you not to declare the other methods.
Step-by-Step Reasoning
class Tree: starts the class definition.
The comment lines are included because Python does not force attribute declarations in advance in the way some other languages do. The exam specifically asks Python candidates to show attribute declarations using comments, so writing lines such as # self.__TreeName: str makes it clear which attributes exist and what type each one stores.
The constructor is written as:
def __init__(self, TreeName, HeightGrowth, MaxHeight, MaxWidth, Evergreen):
self refers to the particular object being created. The remaining parameters are the incoming values used to initialise that object.
Each assignment then stores the parameter value inside the object's private attribute:
self.__TreeName = TreeNameself.__HeightGrowth = HeightGrowthself.__MaxHeight = MaxHeightself.__MaxWidth = MaxWidthself.__Evergreen = Evergreen
This matches the specification exactly: the constructor initialises all five attributes to its parameter values.
Key Takeaways
- A constructor sets up an object when it is created.
- In Python,
__init__is the constructor method. - Private attributes are typically shown with a double underscore prefix.
- If the exam asks for attribute declarations in Python, use comments to show them clearly.
Common Mistakes
- Making the attributes public, for example
self.TreeNameinstead ofself.__TreeName. - Forgetting one of the five attributes.
- Writing the getters in this part even though the question says not to.
- Omitting
selffrom the constructor header. - Assigning the wrong parameter to the wrong attribute.
Things to Be Careful About
- Use the class name
Treeexactly. - Use the Python constructor
__init__, not a method calledConstructor(). - Keep the attributes private with double underscores.
- Include the Python attribute declaration comments because this question specifically asks for them.
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
class Tree:
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
Getter methods are part of encapsulation in object-oriented programming. If attributes are private, outside code should not read them directly. Instead, methods are provided to return the value safely. A getter does not usually change anything; it simply returns the requested attribute.
In Python, the getter body is often just one return statement.
Understanding the Question
This part says the get methods each return the relevant attribute. So you need five methods:
GetTreeName()GetGrowth()GetMaxHeight()GetMaxWidth()GetEvergreen()
Each one must return the correct private attribute from the Tree object. The question is not asking for printing, validation, or extra logic.
Approach
For each method:
- Use the method name exactly as given.
- Include
selfas the parameter because these are instance methods. - Return the matching private attribute.
This produces five very short methods.
Step-by-Step Reasoning
GetTreeName() should return the tree's name, so it returns self.__TreeName.
GetGrowth() should return the yearly growth value, so it returns self.__HeightGrowth.
GetMaxHeight() should return the maximum height, so it returns self.__MaxHeight.
GetMaxWidth() should return the maximum width, so it returns self.__MaxWidth.
GetEvergreen() should return whether the tree keeps its leaves, so it returns self.__Evergreen.
Each method is a direct one-to-one match between the method name and the stored data. That is exactly what a getter should do.
Key Takeaways
- Getter methods allow private data to be accessed in a controlled way.
- A getter normally contains only a
returnstatement. - The returned attribute must match the meaning of the method name exactly.
Common Mistakes
- Using
print(...)instead ofreturn. - Returning the wrong attribute from a method.
- Forgetting
selfin the method header. - Trying to access a public name such as
TreeNameinstead of the private__TreeName.
Things to Be Careful About
- Keep the method names exactly as specified in the question.
- Return the private attributes, not the parameter names from the constructor.
- Do not add unnecessary parameters to the getters.
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:
with open("Trees.txt", "r") as FileHandle:
for Line in FileHandle:
Data = Line.strip().split(",")
NewTree = Tree(Data[0], int(Data[1]), int(Data[2]), int(Data[3]), Data[4])
TreeArray.append(NewTree)
except FileNotFoundError:
raise FileNotFoundError
return TreeArray
See program code
Background Concept
This question combines sequential file processing with object creation. A text file can be read line by line. Each line here is one record describing one tree. Because the data is comma-separated, each line must be split into separate fields before use.
The numeric values in a text file are read as strings, so they must be converted to integers before being stored in integer attributes. The tree name and evergreen value remain as strings.
The question also requires exception handling. If the file does not exist, the program should raise a file-related exception rather than silently failing.
Understanding the Question
You are given the file Trees.txt, where each line looks like:
Beech,30,400,200,No
That means:
- tree name =
Beech - growth per year =
30 - maximum height =
400 - maximum width =
200 - evergreen =
No
The function ReadData() must:
- create an array of
Treeobjects - read all records from the file
- create one
Treeobject for each record - append each object to the array
- raise an exception if the file is not found
- return the finished array
Approach
The cleanest way in Python is:
- Start with an empty list.
- Try to open
Trees.txt. - Loop through each line in the file.
- Remove the newline and split the line at commas.
- Convert the three numeric fields to integers.
- Create a
Treeobject from the five fields. - Append it to the list.
- If the file open fails, raise
FileNotFoundError. - Return the list.
Step-by-Step Reasoning
TreeArray = [] creates the empty array that will store all Tree objects.
The try: block is used because opening a file can fail if the file is missing.
with open("Trees.txt", "r") as FileHandle: opens the file for reading. Using with is good practice because the file is closed automatically afterwards.
for Line in FileHandle: processes each record one at a time.
Line.strip() removes the newline at the end of the line. Without this, the evergreen field on the last item would include \n.
.split(",") breaks the record into a list of five strings. For the first line this becomes roughly:
Data[0] = "Beech"Data[1] = "30"Data[2] = "400"Data[3] = "200"Data[4] = "No"
The constructor call creates an object:
NewTree = Tree(Data[0], int(Data[1]), int(Data[2]), int(Data[3]), Data[4])
The calls to int(...) are essential because HeightGrowth, MaxHeight, and MaxWidth are integers, not strings.
TreeArray.append(NewTree) adds the new object to the array.
If opening the file fails, except FileNotFoundError: catches that specific run-time error, and raise FileNotFoundError raises the exception as required by the question.
Finally, return TreeArray gives the caller the complete array of 9 Tree objects.
Key Takeaways
- Text-file data is read as strings and often needs parsing and type conversion.
- A list of objects is a common way to store multiple structured records in Python.
- Exception handling is used for file access errors.
- When a question says to return an array of objects, you must both create the objects and append them to the array.
Common Mistakes
- Forgetting to convert the numeric fields to integers.
- Not stripping the newline before splitting.
- Appending the raw list of fields instead of a
Treeobject. - Forgetting to return the finished array.
- Catching the exception but not raising it again.
Things to Be Careful About
- The file name must be exactly
Trees.txt. - The file contains names with spaces, such as
Magnolia Grandiflora, but that is fine because the separator is a comma, not a space. - Use the fields in the correct order when calling the constructor.
- Do not accidentally use index
4as an integer field; it is the evergreen string.
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(TreeObject):
if TreeObject.GetEvergreen() == "Yes":
print(f"{TreeObject.GetTreeName()} has a maximum height {TreeObject.GetMaxHeight()} a maximum width {TreeObject.GetMaxWidth()} and grows {TreeObject.GetGrowth()} cm a year. It does not lose its leaves.")
else:
print(f"{TreeObject.GetTreeName()} has a maximum height {TreeObject.GetMaxHeight()} a maximum width {TreeObject.GetMaxWidth()} and grows {TreeObject.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 retrieve data. In this design, the Tree attributes are private, so the procedure should use the getter methods rather than trying to access the attributes directly.
This task also uses selection. The output depends on whether the tree is evergreen. If it is evergreen, the message says it does not lose its leaves. Otherwise, it says it loses its leaves each year.
Understanding the Question
The procedure PrintTrees() takes one Tree object and must output:
- the tree name
- maximum height
- maximum width
- yearly growth
- a different final sentence depending on whether
EvergreenisYesorNo
So the procedure must inspect the object's evergreen value and choose one of two message endings.
Approach
A direct solution is:
- Accept one
Treeobject as the parameter. - Use
GetEvergreen()to decide which message to print. - Use the other getter methods to insert the values into the sentence.
This keeps the procedure simple and matches the OOP design of the rest of the question.
Step-by-Step Reasoning
The procedure header is:
def PrintTrees(TreeObject):
This means the procedure receives one object and stores it in TreeObject.
The if condition checks:
TreeObject.GetEvergreen() == "Yes"
If that is true, the tree keeps its leaves, so the output must end with:
It does not lose its leaves.
If it is false, the tree is not evergreen, so the output must end with:
It loses its leaves each year.
The rest of the sentence is the same in both branches. The getter methods provide each value:
GetTreeName()for the nameGetMaxHeight()for the maximum heightGetMaxWidth()for the maximum widthGetGrowth()for yearly growth
Using an f-string is a neat way to combine the text and the values into one output line.
Key Takeaways
- When attributes are private, other code should access them through getters.
- An
ifstatement is used when output changes according to data. - Keep shared parts of the message accurate and only vary the part that depends on the condition.
Common Mistakes
- Checking for
"No"but printing the evergreen sentence, or vice versa. - Printing the object itself instead of using its getters.
- Leaving out one of the required fields such as maximum width.
- Writing
TreeObject.__TreeName, which breaks encapsulation.
Things to Be Careful About
- The question stores evergreen as a string, so compare with
"Yes"or"No"exactly. - Use the correct getter for each piece of data.
- Keep the message wording consistent so the output matches the required format closely.
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 the overall flow of a solution by calling previously written functions and procedures in the correct order. In Python, if a function returns a list, that list can be stored in a variable and then indexed to access individual objects.
Python lists are zero-indexed, so the first item is at position 0.
Understanding the Question
You are told exactly what the main program must do:
- call
ReadData() - store the returned array
- call
PrintTrees()with the first object in that array
So this is not asking for new logic; it is asking you to connect together the code already written in earlier parts.
Approach
The required sequence is:
- assign the return value of
ReadData()to a variable such asTreeArray - pass the first tree object,
TreeArray[0], intoPrintTrees()
That fully satisfies the task.
Step-by-Step Reasoning
TreeArray = ReadData() calls the file-reading function. Because ReadData() returns the list of Tree objects, TreeArray will refer to that returned list.
PrintTrees(TreeArray[0]) selects the first object in the list and passes it to the output procedure.
Because Python indexing starts at 0, this first object is the tree from the first record in the file.
Key Takeaways
- Main programs often just sequence function calls.
- A returned array can be stored and then indexed.
- Python uses zero-based indexing.
Common Mistakes
- Using
TreeArray[1]for the first element. - Forgetting to store the return value from
ReadData(). - Passing the whole array into
PrintTrees()instead of one object.
Things to Be Careful About
- The first object is
TreeArray[0], notTreeArray[1]. - Make sure
ReadData()is called beforePrintTrees().
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
Using Trees.txt with the main program from 2(d)(i):
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 a test-output question, you do not write new code logic. Instead, you trace what the existing program will do with the given data. That means following the function calls, finding which data record is used, and then determining which branch of any if statement will run.
Understanding the Question
The main program from part (d)(i) does two things:
- reads all tree records from
Trees.txt - prints the first object in the returned array
The first record in the file is:
Beech,30,400,200,No
So the first object represents a Beech tree with growth 30, maximum height 400, maximum width 200, and evergreen value No.
Approach
Trace the data into PrintTrees():
- first tree = Beech object
- evergreen value =
No - therefore use the non-evergreen message
Then substitute the values into the output sentence.
Step-by-Step Reasoning
From the file, the first object has:
- name:
Beech - max height:
400 - max width:
200 - yearly growth:
30 - evergreen:
No
PrintTrees() checks whether evergreen is Yes. For Beech it is No, so the else branch runs.
That branch outputs:
Beech has a maximum height 400 a maximum width 200 and grows 30 cm a year. It loses its leaves each year.
That is the console output you should expect in the screenshot.
Key Takeaways
- To predict output, combine the data file contents with the control flow of the program.
- The first file record becomes the first object in the array.
- The evergreen value controls the final sentence.
Common Mistakes
- Using the wrong record from the file.
- Forgetting that Python lists start at index
0. - Choosing the evergreen sentence when the value is
No.
Things to Be Careful About
- The main program prints only the first object here, not all objects.
- Match the wording from
PrintTrees()exactly, because screenshot questions usually depend on exact output text.
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):
RequiredHeight = int(input("Enter the maximum tree height in cm "))
RequiredWidth = int(input("Enter the maximum tree width in cm "))
RequiredEvergreen = input("Enter Yes for evergreen or No for not evergreen ")
SuitableTrees = []
for Item in TreeArray:
if Item.GetMaxHeight() <= RequiredHeight and Item.GetMaxWidth() <= RequiredWidth and Item.GetEvergreen() == RequiredEvergreen:
SuitableTrees.append(Item)
PrintTrees(Item)
if len(SuitableTrees) == 0:
print("No trees meet your requirements.")
See program code
Background Concept
This is a filtering problem. A collection of objects is scanned one by one, and each object is tested against a set of conditions. Any object that passes all conditions is added to a new result array.
Because the array is simply checked item by item, this is a linear traversal. The important logic point is the compound condition: all three requirements must be true, so and is needed.
Understanding the Question
The user enters three requirements:
- maximum allowed tree height
- maximum allowed tree width
- whether the tree must be evergreen or not
A tree is suitable only if:
- its maximum height is not more than the user's limit
- its maximum width is not more than the user's limit
- its evergreen value matches the user's choice
You must create a new array of suitable trees, print each suitable tree, and print a message if none are suitable.
Approach
A good structure is:
- Read the three requirements.
- Create an empty array
SuitableTrees. - Loop through every
Treeobject in the input array. - Use one
ifstatement with all three tests joined byand. - If the tree matches, append it and print it.
- After the loop, if the new array is empty, output a suitable message.
Step-by-Step Reasoning
The first three lines read the user's criteria. Height and width must be converted to integers because they will be compared numerically with the tree object's integer attributes.
SuitableTrees = [] creates the new array the question requires.
The for Item in TreeArray: loop checks every tree object.
The key condition is:
Item.GetMaxHeight() <= RequiredHeight and Item.GetMaxWidth() <= RequiredWidth and Item.GetEvergreen() == RequiredEvergreen
Each part matters:
<= RequiredHeightmeans the tree's maximum height is not more than the user's maximum.<= RequiredWidthmeans the tree's maximum width is not more than the user's maximum.== RequiredEvergreenmeans the tree matches the evergreen choice exactly.
If all three are true, the tree is suitable. Then two actions happen:
SuitableTrees.append(Item)stores it in the new array.PrintTrees(Item)outputs its details.
After the loop, if len(SuitableTrees) == 0: checks whether no trees matched. If so, the program prints a message to tell the user there were no suitable results.
Key Takeaways
- Filtering a list means scanning every item and testing each one against conditions.
- If every condition must be true, use
and. - When a question says to create a new array of matches, you must actually store the matches, not just print them.
Common Mistakes
- Using
orinstead ofand, which would allow trees matching only one condition. - Using
<instead of<=, which would wrongly reject an exact boundary match. - Forgetting to create the new array of suitable trees.
- Printing matches but never appending them.
- Forgetting the no-results message.
Things to Be Careful About
- Height and width inputs should be converted to integers.
- Compare the evergreen values consistently, for example
YesandNo. - The procedure does not return the array here; it creates it and uses it internally as required.
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
def ChooseTree(TreeArray):
RequiredHeight = int(input("Enter the maximum tree height in cm "))
RequiredWidth = int(input("Enter the maximum tree width in cm "))
RequiredEvergreen = input("Enter Yes for evergreen or No for not evergreen ")
SuitableTrees = []
for Item in TreeArray:
if Item.GetMaxHeight() <= RequiredHeight and Item.GetMaxWidth() <= RequiredWidth and Item.GetEvergreen() == RequiredEvergreen:
SuitableTrees.append(Item)
PrintTrees(Item)
if len(SuitableTrees) == 0:
print("No trees meet your requirements.")
else:
ChosenName = input("Enter the name of the tree you would like to buy ")
BoughtHeight = int(input("Enter the height of the tree you would like to buy in cm "))
for Item in SuitableTrees:
if Item.GetTreeName() == ChosenName:
Years = (Item.GetMaxHeight() - BoughtHeight) / Item.GetGrowth()
print(f"Your tree should be full height in approximately {Years:g} years")
break
See program code
Background Concept
This is an extension of the earlier filtering procedure. After building a list of suitable objects, the program performs a linear search through that filtered list to find the tree the user has chosen by name.
The growth calculation is:
That works because the remaining height to grow is divided by the number of centimetres gained each year.
Understanding the Question
After the list of suitable trees has been shown, the procedure must now do three extra things:
- input the name of the chosen tree
- input the height when it is bought
- calculate and output how many years it will take to reach maximum height
The chosen tree should come from the suitable list that was just produced.
Approach
The sensible amendment is:
- Keep the original filtering code.
- Only continue if there is at least one suitable tree.
- Input the chosen name and starting height.
- Search through
SuitableTreesfor the matching name. - Use the maximum height and yearly growth of that object to calculate the number of years.
- Output the result.
Step-by-Step Reasoning
The first part of the procedure remains unchanged: it collects the criteria, filters the trees, stores matches in SuitableTrees, and prints each one.
The extra code belongs after that.
if len(SuitableTrees) == 0: handles the no-match case. If there are no suitable trees, the program cannot ask the user to choose one from the list, so it just prints the message.
The else: branch means there is at least one suitable tree.
ChosenName = input(...) stores the selected tree name.
BoughtHeight = int(input(...)) stores the starting height as an integer.
The loop:
for Item in SuitableTrees:
searches only the filtered list, which matches the wording of the question.
When the names match, the program calculates:
Years = (Item.GetMaxHeight() - BoughtHeight) / Item.GetGrowth()
Item.GetMaxHeight() - BoughtHeight is the remaining growth needed.
Dividing by Item.GetGrowth() gives the number of years.
Using {Years:g} in the f-string is useful because it prints 12 instead of 12.0 while still printing values like 3.75 correctly.
break stops searching once the chosen tree has been found.
Key Takeaways
- After filtering a list, you can search the filtered results for a specific item.
- Growth problems often use remaining amount divided by rate.
- It is good practice not to continue into selection code when there are no results.
Common Mistakes
- Searching the full original array instead of the suitable array.
- Using maximum width instead of maximum height in the calculation.
- Forgetting to subtract the starting height before dividing.
- Using integer division, which would lose decimal accuracy.
Things to Be Careful About
- Use normal division
/, not integer division. - The chosen tree name must be compared exactly to the stored name.
- This code assumes the starting height is below the maximum height; validation is not required here because the question does not ask for it.
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)
Using inputs 400, 200, Yes, 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.
Enter the maximum tree height in cm 400
Enter the maximum tree width in cm 200
Enter Yes for evergreen or No for not evergreen Yes
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 would like to buy Blue Conifer
Enter the height of the tree you would like to buy in cm 100
Your tree should be full height in approximately 3.75 years
See program code and expected output
Background Concept
This is an integration step. Instead of testing one procedure in isolation, you are now checking that the main program calls the procedures in the right order and that the final output matches the supplied test data.
To predict the output, you combine:
- the file contents
- the main-program sequence
- the filtering logic in
ChooseTree() - the calculation for years to full height
Understanding the Question
You must amend the main program so it calls ChooseTree() after reading the data. Then you test it with these inputs:
- maximum height =
400 - maximum width =
200 - evergreen =
Yes - chosen tree =
Blue Conifer - starting height =
100
The existing main program from part (d) still prints the first tree first, so the console output begins with the Beech line.
Approach
There are two pieces here:
- Amend the main program by adding the call to
ChooseTree(TreeArray). - Trace the specified inputs through the program to determine which trees match and what the final calculation gives.
Step-by-Step Reasoning
The amended main program is:
TreeArray = ReadData()PrintTrees(TreeArray[0])ChooseTree(TreeArray)
The first output is therefore still for the first tree in the file, Beech.
Now apply the test criteria to all trees in Trees.txt.
A tree must have:
- maximum height
<= 400 - maximum width
<= 200 - evergreen =
Yes
Check the evergreen trees:
Holly: height600→ too tall, rejectMagnolia Grandiflora: height500→ too tall, rejectPhotinia: width400→ too wide, rejectBlue Conifer: height250, width50, evergreenYes→ acceptGreen Conifer: height300, width150, evergreenYes→ accept
So the suitable trees printed are:
- Blue Conifer
- Green Conifer
The chosen tree is Blue Conifer, bought at 100 cm.
Its maximum height is 250 cm and it grows 40 cm per year.
Remaining growth needed:
Years to full height:
So the final output is:
Your tree should be full height in approximately 3.75 years
Prompt wording can vary slightly depending on your program, but the important listed trees and the final calculated result should be the same.
Key Takeaways
- Testing integrated code means following the whole chain of calls.
- Filter conditions should be checked systematically against each record.
- The final years value comes from remaining growth divided by annual growth.
Common Mistakes
- Forgetting to keep the earlier
PrintTrees(TreeArray[0])call in the main program. - Including trees that fail one of the three requirements.
- Calculating
250 / 40instead of(250 - 100) / 40. - Calling
ChooseTrees()with the wrong name if you copied the typo from the question instead of using the procedure you actually wrote.
Things to Be Careful About
- In Python, the first object remains
TreeArray[0]. - The procedure name in your code should match the one you declared; although the question text says
ChooseTrees(), the defined procedure isChooseTree(). - Output wording for prompts may differ, but the selected trees and the
3.75years result must be correct.
3 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 = ['' for _ in range(20)]
QueueHead = -1
QueueTail = -1
See program code
Background Concept
A linear queue stores items in first-in, first-out order. For an array-based queue, two pointers are usually kept:
QueueHeadpoints to the first item that would be removed next.QueueTailpoints to the last item that was inserted most recently.
When the queue is empty, both pointers are often set to a special value such as -1. The array also needs an initial value in every element. Because this queue stores strings, an empty string is a suitable null value.
Understanding the Question
This part only asks for the main program initialisation. The stem already tells us the queue is global, one-dimensional, stores strings, and must have space for 20 elements. So the required code is simply:
- create the array with 20 string slots
- give each slot a null value
- set both pointers to
-1
No enqueue or dequeue logic is needed yet.
Approach
Use a Python list of length 20 to represent the queue. Fill it with empty strings. Then set QueueHead and QueueTail to -1 to show that the queue currently contains no items.
Step-by-Step Reasoning
QueueData = ['' for _ in range(20)]
- creates 20 positions
- each position contains an empty string
- this matches the requirement for a suitable null value for string data
QueueHead = -1
- means there is no first item yet
QueueTail = -1
- means there is no last item yet
Together, these values clearly represent an empty queue before any data is entered.
Key Takeaways
- A queue needs storage plus pointer variables.
-1is a common sentinel value for an empty queue.- The null value should match the data type being stored.
Common Mistakes
- Using the wrong size for the array, such as 19 or 21 elements.
- Initialising the array with numeric values instead of string values.
- Setting only one pointer to
-1and forgetting the other.
Things to Be Careful About
- The queue must have space for exactly 20 elements.
- Because the queue stores strings,
''is more appropriate than0. - Keep the variable names exactly as given in the question:
QueueData,QueueHead,QueueTail.
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
else:
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. In a linear array-based queue:
- the queue is full when the tail has reached the last array index
- the queue is empty when both pointers are
-1 - the first insertion into an empty queue must set the head pointer as well as the tail pointer
A queue of size 20 in Python uses indices 0 to 19, so index 19 is the last valid position.
Understanding the Question
The function receives one item to add. It must:
- return
Falseif the queue is full - otherwise insert the item
- update the correct pointer values
- return
True
Because the queue and pointers are global, the function must update those global values directly.
Approach
Check fullness first, because no insertion is possible if the tail is already at the end of the array. If there is space:
- if the queue is currently empty, set
QueueHeadto0 - move
QueueTailon by one - store the new item in
QueueData[QueueTail] - return
True
Step-by-Step Reasoning
global QueueData, QueueHead, QueueTail
- tells Python that the function is changing the global queue structure, not creating local variables with the same names
if QueueTail == 19:
- checks whether the last position is already in use
- if it is, no more items can be added in a linear queue
return False
- gives the required failure result
if QueueHead == -1:
- this means the queue is empty before insertion
- the first valid item will go into position
0 - so the head must be set to
0
QueueTail += 1
- moves the tail to the next free slot
- when the queue is empty,
QueueTailchanges from-1to0
QueueData[QueueTail] = DataToAdd
- stores the new value at the tail position
return True
- reports that insertion succeeded
Key Takeaways
- For a linear queue, the full condition depends on the tail reaching the last array index.
- The first insertion is a special case because both head and tail must end up pointing to the new item.
- Queue operations often return a status value to show success or failure.
Common Mistakes
- Forgetting to set
QueueHeadwhen inserting into an empty queue. - Storing the item before moving
QueueTail, which can use the wrong index. - Using
20instead of19as the final valid index. - Returning strings such as
'True'instead of the Boolean valueTrue.
Things to Be Careful About
- This is a linear queue, not a circular queue, so freed spaces are not reused.
- The array has 20 elements but valid indices are
0to19. - The function name and pointer names should match the question exactly.
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() removes and returns the item at the front of the queue. In a queue, the front is always the head. There are two important cases:
- the queue is empty, so nothing can be removed
- the queue has at least one item, so the head item is returned and the pointers are updated
If the removed item was the only item in the queue, then the queue becomes empty again, so both pointers must go back to -1.
Understanding the Question
This function must return 'false' if the queue is empty. Otherwise it must:
- get the next item from the queue
- update the pointer or pointers correctly
- return that item
The stem says the queue stores strings, so returning the string 'false' is the required empty-queue sentinel in this question.
Approach
Start by checking whether QueueHead is -1. If so, the queue is empty. Otherwise:
- read the value at
QueueData[QueueHead] - decide whether this is the last remaining item
- if it is the last item, reset both pointers to
-1 - otherwise, move
QueueHeadone position to the right - return the removed item
Step-by-Step Reasoning
if QueueHead == -1:
- checks whether the queue is empty
- in this queue design,
-1means there is no valid head item
return 'false'
- matches the wording of the question exactly
Item = QueueData[QueueHead]
- saves the front item before changing any pointers
- this is the value that must be returned
if QueueHead == QueueTail:
- means head and tail point to the same element
- so there is exactly one item in the queue
QueueHead = -1 and QueueTail = -1
- reset the queue to the empty state after that item is removed
else: QueueHead += 1
- if there were more items behind it, the next item becomes the new front
return Item
- returns the removed value
Key Takeaways
- Dequeue always removes from the head, not the tail.
- A one-item queue is a special case because removing that item empties the queue.
- Saving the item before updating pointers is essential.
Common Mistakes
- Incrementing
QueueHeadbefore reading the item, which skips the front value. - Resetting only one pointer when the last item is removed.
- Returning
Falseinstead of the required string'false'. - Removing from the tail instead of the head.
Things to Be Careful About
- The question explicitly says the return value for an empty queue is
'false', so use that exact value consistently. - Keep the queue order as first in, first out.
- Because the queue is global, the function must update the shared pointer values, not local copies.
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):
Data = input('Enter data')
Total = 0
for Index in range(6):
if Index % 2 == 0:
Total += int(Data[Index])
else:
Total += int(Data[Index]) * 3
CheckDigit = Total // 10
if CheckDigit == 10:
CheckCharacter = 'X'
else:
CheckCharacter = str(CheckDigit)
if Data[6] == CheckCharacter:
if Enqueue(Data[0: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 brings together two ideas:
- validating data using a check digit
- storing only valid items in a queue
A check digit is an extra character added to data so the program can test whether the data is likely to be correct. Here, the first six characters are digits. The algorithm multiplies digits in even positions by 1 and digits in odd positions by 3, adds the products, divides by 10 using integer division, and uses that result as the check digit. If the result is 10, the check digit becomes 'X'.
A valid 7-character input therefore has:
- six data digits
- one final check character that matches the calculated value
Understanding the Question
The routine must read exactly ten strings from the user. For each one it must:
- calculate the check digit from the first six characters
- compare that with the seventh character
- if valid, store only the first six characters in the queue using
Enqueue() - output a message if inserted
- output a message if the queue is full
- ignore invalid inputs so they are not stored
- count how many invalid inputs were entered
- output the invalid count at the end
So this is not just validation. It also includes queue insertion and summary output.
Approach
Use a procedure StoreItems() with a loop that runs 10 times. Inside the loop:
- read the 7-character string
- calculate the weighted total from positions
0to5 - calculate the check digit with integer division by 10
- convert
10to'X', otherwise convert the number to a character string - compare with the seventh character,
Data[6] - if valid, call
Enqueue()with the first six characters only - if invalid, increase the invalid counter
- after the loop, print the number of invalid items
Step-by-Step Reasoning
Invalid = 0
- starts the counter before any inputs are processed
for Count in range(10):
- ensures exactly ten items are entered, as required
Data = input('Enter data')
- reads one 7-character string from the user
Total = 0
- starts the weighted sum for this one item
for Index in range(6):
- processes only the first six characters
- the check digit at position
6is not included in the calculation
if Index % 2 == 0:
- even positions
0,2,4use multiplier 1
else:
- odd positions
1,3,5use multiplier 3
int(Data[Index])
- converts each character digit to an integer so arithmetic can be done
CheckDigit = Total // 10
//performs integer division, which matches the instruction to round down
if CheckDigit == 10:
- the question says
10must be replaced by'X'
if Data[6] == CheckCharacter:
- compares the entered seventh character with the calculated one
Enqueue(Data[0:6])
- stores only the first six characters in the queue
- the check digit is removed before storage, exactly as required
if Enqueue(...) / else
- handles both possibilities: inserted successfully or queue full
Invalid += 1
- happens only when the check digit does not match
- queue-full items are valid items that could not be inserted, not invalid data
print('There were', Invalid, 'Invalid items')
- outputs the final total after all ten entries have been processed
Key Takeaways
- A check digit is calculated from the main data and compared with the final character.
- Integer division is the correct way to round down in Python.
- Validating input and storing it are separate steps.
- Only the six data digits are stored in the queue, not the check digit.
Common Mistakes
- Including the seventh character in the check-digit calculation.
- Using
% 10instead of// 10; this question does not ask for a remainder. - Forgetting to change check digit
10into'X'. - Enqueueing the full 7-character string instead of the first six characters only.
- Counting queue-full items as invalid inputs.
Things to Be Careful About
- String positions in Python start at
0, which matches the question's position numbering. - Use
Data[6]for the check character andData[0:6]for the six-digit data. - The comparison must be between characters, so numeric check digits need converting to strings.
- The queue could become full in general, even though it does not in the supplied test data.
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
QueueData = ['' for _ in range(20)]
QueueHead = -1
QueueTail = -1
StoreItems()
Item = Dequeue()
if Item == 'false':
print('Queue is empty')
else:
print('Item code', Item)
See program code
Background Concept
The main program controls the sequence in which subroutines are used. Here, it must first build the queue by calling StoreItems(), then remove the front item by calling Dequeue(), and finally display a message based on what Dequeue() returned.
A common pattern is:
- call a subroutine that processes data
- store the return value from another subroutine
- use selection to decide what to output
Understanding the Question
This part does not ask for new queue logic. It asks you to amend the main program so that it:
- calls
StoreItems() - calls
Dequeue() - outputs a suitable message if the queue was empty
- otherwise outputs the value returned
Since Dequeue() returns 'false' when the queue is empty, the main program needs to test for that exact value.
Approach
Start with the queue initialisation from part (a), because the main program still needs that setup. Then:
- call
StoreItems()to read and store valid data - call
Dequeue()and save the returned value in a variable - use
ifto test whether the returned value is'false' - output the correct message for each case
Step-by-Step Reasoning
QueueData = ['' for _ in range(20)]
- sets up the queue storage
QueueHead = -1 and QueueTail = -1
- start with an empty queue
StoreItems()
- reads the ten inputs
- validates them
- stores valid six-digit values in queue order
Item = Dequeue()
- removes the front item if one exists
- or returns
'false'if the queue is empty
if Item == 'false':
- tests the empty-queue sentinel from part (c)
print('Queue is empty')
- handles the empty case
else: print('Item code', Item)
- outputs the removed item when the queue was not empty
Key Takeaways
- The main program coordinates previously written procedures and functions.
- A return value often controls the next decision.
- Integration questions test whether separate routines work together correctly.
Common Mistakes
- Calling
Dequeue()beforeStoreItems(), which would usually empty nothing. - Forgetting to store the return value from
Dequeue(). - Testing for
Falseinstead of the string'false'. - Printing the item without handling the empty case.
Things to Be Careful About
- Use the same sentinel value that
Dequeue()actually returns. - If you show the main program fragment, include the initialisation as well so it is complete.
- Keep the output message sensible and clearly linked to the returned value.
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
Using the ten inputs given, the expected 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
There were 4 Invalid items; Item code 999999
Background Concept
A trace question for a practical paper asks you to work out what the program would display when run with specific inputs. Here the output depends on three things:
- whether each 7-character input is valid according to the check-digit rule
- which valid six-digit values are successfully added to the queue
- what happens when one value is dequeued at the end
Because the queue is first-in, first-out, the first valid stored item is the one that will later be removed and displayed.
Understanding the Question
You are given ten inputs in a fixed order. The program:
- reads each input
- validates it
- prints
Inserted itemonly for valid items that are enqueued - counts invalid items
- after all input, prints the invalid count
- dequeues one item and prints it if the queue is not empty
So to get the final console output, we must test each code, decide whether it is valid, and keep the valid six-digit values in queue order.
Approach
For each input:
- calculate the weighted sum of the first six digits
- divide by 10 using integer division to get the check digit
- replace 10 with
X - compare with the seventh character
Then build the queue from the valid items only. Finally, remove the front item with Dequeue().
Step-by-Step Reasoning
Check each input in turn.
999999X
- weighted sum = 9 + 27 + 9 + 27 + 9 + 27 = 108
- check digit = 108 // 10 = 10, so use
X - valid, so
999999is enqueued - output:
Inserted item
1251484
- weighted sum = 1 + 6 + 5 + 3 + 4 + 24 = 43
- check digit = 4
- valid, so
125148is enqueued - output:
Inserted item
5500212
- weighted sum = 5 + 15 + 0 + 0 + 2 + 3 = 25
- check digit = 2
- valid, so
550021is enqueued - output:
Inserted item
0033585
- weighted sum = 0 + 0 + 3 + 9 + 5 + 24 = 41
- check digit = 4
- entered check digit is
5 - invalid, not stored
9845788
- weighted sum = 9 + 24 + 4 + 15 + 7 + 24 = 83
- check digit = 8
- valid, so
984578is enqueued - output:
Inserted item
6666666
- weighted sum = 6 + 18 + 6 + 18 + 6 + 18 = 72
- check digit = 7
- entered check digit is
6 - invalid, not stored
3258746
- weighted sum = 3 + 6 + 5 + 24 + 7 + 12 = 57
- check digit = 5
- entered check digit is
6 - invalid, not stored
8111022
- weighted sum = 8 + 3 + 1 + 3 + 0 + 6 = 21
- check digit = 2
- valid, so
811102is enqueued - output:
Inserted item
7568557
- weighted sum = 7 + 15 + 6 + 24 + 5 + 15 = 72
- check digit = 7
- valid, so
756855is enqueued - output:
Inserted item
0012353
- weighted sum = 0 + 0 + 1 + 6 + 3 + 15 = 25
- check digit = 2
- entered check digit is
3 - invalid, not stored
Invalid items = 4.
Queue contents after StoreItems():
999999125148550021984578811102756855
Dequeue() removes the first item, which is 999999, so the final line is Item code 999999.
The prompt text appears on the same line as each input because the program uses input('Enter data'), which displays the prompt and then the user types on that line.
Key Takeaways
- To predict output, trace the logic in the exact order the program executes it.
- Queue order matters: the earliest valid inserted item is removed first.
- Console prompts from
input()usually appear on the same line as the entered data.
Common Mistakes
- Treating the check digit as the remainder instead of the integer-divided result.
- Forgetting that
10becomesX. - Enqueueing the full 7-character code instead of the first six characters.
- Dequeueing the most recent item instead of the first item.
- Counting valid-but-not-inserted items as invalid, even though the queue never becomes full here.
Things to Be Careful About
- The required output depends on the exact messages used in the code, such as
Inserted itemandThere were ... Invalid items. - Because the queue size is 20, none of these ten inputs causes a full-queue message.
- The final printed code is the six-digit stored value, not the original 7-character input with the check digit.