Computer Science 9618/21 — October/November 2024
Cambridge AS Level · Fundamental Problem-solving and Programming Skills · worked solutions for every part, with the mark scheme
Topics Programming · Data Types and Structures · Software Development · Algorithm Design and Problem-solving
A program will calculate the tax payable based on the cost of an item.
Calculations will occur at many places in the program and these involve the use of one of three tax rates.
Tax rate values represent a percentage. For example, a tax rate value of 5.23 represents 5.23%. In this case, the tax payable on an item costing 5.23.
Tax rate values are used at several places within the program. One example is given in pseudocode as follows:
HighRate ← FALSE
CASE OF ItemCost
<= 50 : TaxRate ← 3.75 // tax rate of 3.75%
<= 200 : TaxRate ← 5.23 // tax rate of 5.23%
> 200 : TaxRate ← 6.25 // tax rate of 6.25%
HighRate ← TRUE
ENDCASE
TaxPayable ← ItemCost * TaxRate // tax payable
The pseudocode contains a logical error.
Identify the error and suggest a correction.
Error ..........................................................................................................................................
Correction .................................................................................................................................
Answer
- Error:
TaxPayable ← ItemCost * TaxRateusesTaxRateas if it were a decimal value, but it is stored as a percentage. - Correction:
TaxPayable ← ItemCost * TaxRate / 100
TaxRate is used as a percentage; correct calculation: TaxPayable ← ItemCost * TaxRate / 100
Background Concept
A logic error happens when a program runs, but produces the wrong result because the method is incorrect. In arithmetic questions, a very common logic error is using a percentage as though it were already a multiplier.
For example:
5.23%means5.23 per 100- as a calculation factor, that is
5.23 / 100 = 0.0523
So if an item costs $100, then tax at 5.23% is:
If you forget the division by 100, you get:
which is clearly wrong.
Understanding the Question
The question gives pseudocode where TaxRate is chosen correctly using a CASE OF statement. It also clearly tells you that values such as 3.75, 5.23 and 6.25 represent percentages.
You are asked to:
- identify the logical error
- give a correction
So the key is not the CASE selection itself. The problem is in the final line where TaxPayable is calculated.
Approach
Read the description carefully and compare it with the arithmetic used in the pseudocode.
The description says TaxRate values are percentages. Therefore, when calculating tax, the rate must be converted from a percentage into its fractional form by dividing by 100.
So the approach is:
- Spot where
TaxRateis used in a calculation. - Check whether that use matches the meaning of a percentage.
- Replace it with a correct percentage calculation.
Step-by-Step Reasoning
The relevant line is:
TaxPayable ← ItemCost * TaxRate
Now test it mentally using the example given in the question:
ItemCost = 100TaxRate = 5.23
Substituting into the pseudocode gives:
But the question explicitly says the tax on $100 at 5.23% should be $5.23, not $523.
That proves the expression is logically wrong.
To fix it, divide the percentage by 100 during the calculation:
TaxPayable ← ItemCost * TaxRate / 100
Now the same example gives:
which matches the intended result.
An alternative design would be to store the tax rates as 0.0375, 0.0523, and 0.0625 instead, but this part specifically asks for the correction to the shown pseudocode, so the direct correction is to divide by 100.
Key Takeaways
- A percentage must be converted to a fraction before using it as a multiplier.
- Logic errors often appear in formulas even when the program structure is correct.
- Testing with a known example is a good way to spot this kind of mistake.
Common Mistakes
- Writing that the error is in the
CASEstatement when the selection of bands is actually fine. - Saying only “the calculation is wrong” without explaining that the rate is a percentage.
- Correcting it to
TaxPayable ← ItemCost / TaxRate, which does not represent percentage tax. - Changing the stored values instead of correcting the shown line, when the question asks for a correction to the pseudocode.
Things to Be Careful About
- The question defines the meaning of the tax rate values very precisely. You must use that wording: they are percentages, not decimal multipliers.
- In CIE pseudocode, assignment uses
←, not=. - If you describe the correction in words, make sure it is unambiguous: divide the tax rate by 100 or divide the whole product by 100.
During the design of the program, tax rate values have been used wherever they are needed as shown in the pseudocode example above. Tax rates do not change while the program runs.
Identify a more appropriate way of representing the tax rate values in the final program.
Answer
- Represent the tax rates as named constants.
Named constants
Background Concept
A constant is a named value that does not change while a program runs. This is different from a variable, whose value can change.
Using named constants is good practice when:
- the same value is needed in several places
- the value has a fixed meaning
- the value should not be altered accidentally
For example, instead of writing 5.23 everywhere, a program might use a constant such as StandardTaxRate.
Understanding the Question
The question says the tax rate values:
- are used in many places in the program
- do not change while the program runs
That wording is a direct clue. If a value is fixed and reused, it should normally be represented as a constant rather than repeating the literal number throughout the program.
Approach
Look at the two important facts in the stem:
- the tax rates are reused
- the tax rates never change during execution
Those are the exact characteristics of constants, so the most appropriate representation is named constants.
Step-by-Step Reasoning
The current design shows the literal values directly in the pseudocode:
3.755.236.25
Because these are fixed tax rates, the final program should not keep scattering these numbers throughout the code. Instead, each should be given a constant name.
That means the answer is simply:
- use named constants for the tax rates
The word named matters because it is better than just saying “constants”; meaningful names make the code easier to read and maintain.
Key Takeaways
- Fixed values that are reused should normally be stored as named constants.
- Constants improve clarity and help prevent accidental changes.
- In exam questions, phrases like “do not change while the program runs” strongly suggest constants.
Common Mistakes
- Answering “variables”, which is wrong because variables are for values that can change.
- Answering “array” or “record”, which are data structures rather than the most appropriate representation here.
- Saying only “global” without identifying that the values should be constants.
Things to Be Careful About
- The question asks for a way of representing the tax rate values, not for actual pseudocode declarations.
- “Named constants” is stronger than just “constants” because it reflects good program design.
- Do not confuse a constant with a literal number written directly in code; the improvement is to give the value a name.
Answer
- Each tax rate value is defined once only, so if a rate changes it only needs to be amended in one place.
- Named constants make the program easier to read because the tax rates can have meaningful identifiers.
- Using constants avoids repeating the numeric values throughout the program, reducing the chance of mistakes or inconsistent values.
Single-point maintenance, improved readability, and less risk from repeated values
Background Concept
One of the goals of good program design is to make code:
- easy to understand
- easy to maintain
- less error-prone
Named constants help with all three.
Instead of writing the same number many times, a programmer stores it once and refers to it by name. This avoids what are often called magic numbers: unexplained numeric values scattered through code.
For example, a name such as HighTaxRate immediately tells the reader what the value means, whereas 6.25 on its own does not explain its role.
Understanding the Question
Part (b)(i) asks for a better representation of the tax rates, and part (b)(ii) asks for the benefits of that choice with reference to this program.
That means your answer should not be generic. You should link the benefits directly to the fact that:
- there are three tax rates
- they are used in many places
- they do not change during program execution
Approach
Think about what problems repeated literal values can cause in this specific tax program, then explain how named constants solve them.
The strongest benefits are:
- easier maintenance if the tax percentages change in the future
- better readability through meaningful names
- fewer errors from repeating the same numbers in many places
Step-by-Step Reasoning
If the tax rates are written directly everywhere they are needed, the program has repeated literals such as 3.75, 5.23, and 6.25 throughout the code.
That leads to several issues.
1. Easier maintenance
Suppose the 5.23% rate changes in future. If the value appears in many places, the programmer has to search for every occurrence and update all of them. That is slow and easy to get wrong.
With a named constant, the value is stored once. The programmer changes it in one place only, and every part of the program automatically uses the new rate.
2. Better readability
A named constant such as LowTaxRate, MiddleTaxRate, or HighTaxRate makes the program more self-explanatory. Someone reading the code can understand the role of the value immediately.
A bare number like 5.23 does not explain whether it is tax, discount, interest, or something else.
3. Fewer mistakes
If values are typed repeatedly, one occurrence might be entered incorrectly, or one copy might be updated while another is forgotten. That creates inconsistent behaviour in different parts of the program.
Using constants removes this duplication and makes the program more reliable.
These are exactly the benefits the examiner is looking for because the question says the values are used at several places within the program.
Key Takeaways
- Named constants are especially useful for fixed values used in multiple places.
- They improve maintainability, readability, and reliability.
- In design questions, always connect your benefits to the scenario given rather than giving generic statements only.
Common Mistakes
- Giving only one benefit when several marks are available.
- Stating “it is faster” without justification; performance is not the main benefit here.
- Forgetting to refer to this specific program, which repeatedly uses tax rate values.
- Saying constants are better because they can change during execution, which is the opposite of what a constant is.
Things to Be Careful About
- The question asks for benefits of the answer to part (b)(i), so your explanation must clearly be about named constants.
- Maintenance here means future amendment of the program, not changing values while the current run is happening.
- Use benefits that are genuinely relevant to the scenario: repeated fixed tax values used in many places.
Give the appropriate data type for the variables in the following table, as used in the pseudocode:
| Variable name | Data type |
|---|---|
| HighRate | |
| TaxPayable |
Answer
| Variable name | Data type |
|---|---|
| HighRate | BOOLEAN |
| TaxPayable | REAL |
HighRate: BOOLEAN; TaxPayable: REAL
Background Concept
Choosing the correct data type means matching the type to the kind of value a variable stores.
Common basic data types in pseudocode include:
- BOOLEAN: stores
TRUEorFALSE - INTEGER: stores whole numbers only
- REAL: stores numbers with a fractional part
- STRING: stores text
To identify a type, look at how the variable is assigned and used.
Understanding the Question
You are given two variable names from the pseudocode:
HighRateTaxPayable
You must give the most appropriate data type for each.
The question says “as used in the pseudocode”, so the answer must come from the values assigned in the given code, not from guessing other possible designs.
Approach
For each variable:
- find where it is assigned
- see what kind of value it holds
- choose the matching data type
Step-by-Step Reasoning
HighRate
In the pseudocode:
HighRate ← FALSE
and later:
HighRate ← TRUE
So HighRate only stores TRUE or FALSE. That makes it a BOOLEAN.
TaxPayable
TaxPayable is calculated from:
TaxPayable ← ItemCost * TaxRate / 100
The tax rates include values such as 3.75, 5.23, and 6.25, which are not whole numbers. Therefore the result can include a fractional part.
A variable that may contain decimal values should be REAL.
So the completed table is:
HighRate→BOOLEANTaxPayable→REAL
Key Takeaways
- A variable storing
TRUE/FALSEisBOOLEAN. - A variable storing a value that may have decimals should be
REAL. - Data types are identified from actual use in the code.
Common Mistakes
- Writing
INTEGERforTaxPayable; tax amounts can include decimal values. - Writing
STRINGbecause the variable name looks descriptive; names do not determine type. - Writing “logical” instead of
BOOLEANif the exam expects standard pseudocode data types.
Things to Be Careful About
- Use the exact data type names expected in CIE-style pseudocode:
BOOLEAN,INTEGER,REAL,STRING. TaxPayableis not guaranteed to be a whole number, soREALis safer and more accurate thanINTEGER.- Base your choice on the pseudocode shown, especially the literal values and expressions.
The final CASE condition (> 200) in the pseudocode example could be replaced with a keyword.
Give the keyword.
Answer
OTHERWISE
OTHERWISE
Background Concept
A CASE OF statement is a selection structure used when one variable or expression is being compared against several possible cases.
In CIE pseudocode, the final branch can use the keyword OTHERWISE to mean:
- if none of the earlier cases matched
- do this instead
This is the CASE equivalent of a default or catch-all branch.
Understanding the Question
The original pseudocode ends with:
> 200 : TaxRate ← 6.25
HighRate ← TRUE
The question says this final condition could be replaced with a keyword. That tells you the earlier cases already cover all smaller values, so the last branch is simply the “everything else” case.
Approach
Look at the earlier branches:
<= 50<= 200
Anything not matched by those must fall into the last case. That means the last case does not need an explicit comparison; it can use the default CASE keyword.
Step-by-Step Reasoning
The CASE OF checks ItemCost.
- If
ItemCost <= 50, first branch is used. - Otherwise, if
ItemCost <= 200, second branch is used. - If neither of those is true, the only remaining possibility is the final branch.
So instead of writing > 200, the final branch can be written using the default keyword:
OTHERWISE
That is the standard pseudocode term expected here.
Key Takeaways
OTHERWISEis the default branch in aCASE OFstatement.- Use it when the final case means “all remaining values”.
- It can make selection logic clearer and simpler.
Common Mistakes
- Writing
ELSE, which belongs toIFstatements, notCASE OFin CIE pseudocode. - Writing
DEFAULT, which may be used in some programming languages but is not the CIE pseudocode keyword expected here. - Keeping
> 200when the question specifically asks for the keyword replacement.
Things to Be Careful About
- Use the exact keyword expected by the pseudocode guide:
OTHERWISE. - Do not mix syntax from real programming languages with CIE pseudocode.
- This works here because the earlier conditions already cover the other possibilities.
A program uses three global integer variables HH, MM and SS to represent the current time in hours, minutes and seconds using the 24-hour clock notation.
Midnight would be represented as 00:00:00 (HH:MM:SS). If the variables HH, MM and SS contained the values 16, 30 and 10 respectively, then the time would be 16:30:10 or just after 4.30 in the afternoon.
A procedure Tick() will be called every second.
The procedure Tick() will:
• update the value in SS each time it is called
• update the values in HH and MM as appropriate
• call a procedure CheckAlarm() at the start of each minute
• call a procedure NewDay() whenever the time reaches midnight.
Complete the pseudocode for procedure Tick().
PROCEDURE Tick()
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
ENDPROCEDURE
Answer
PROCEDURE Tick()
SS ← SS + 1
IF SS = 60 THEN
SS ← 0
MM ← MM + 1
CALL CheckAlarm()
IF MM = 60 THEN
MM ← 0
HH ← HH + 1
IF HH = 24 THEN
HH ← 0
CALL NewDay()
ENDIF
ENDIF
ENDIF
ENDPROCEDURE
See completed pseudocode
Background Concept
This is a typical cascading counter problem. Time in a 24-hour clock is stored as three separate integer values:
SSfor seconds, valid range0to59MMfor minutes, valid range0to59HHfor hours, valid range0to23
Each call to Tick() represents one second passing. That means the normal action is to increase SS by 1. However, if SS reaches 60, it must roll over to 0 and cause the next larger unit, MM, to increase. The same idea applies again from minutes to hours.
This kind of logic is handled using nested IF statements:
- first check whether seconds have overflowed
- then, only if needed, check whether minutes have overflowed
- then, only if needed, check whether hours have overflowed
The question also adds two event-triggered procedure calls:
CheckAlarm()must be called at the start of each minuteNewDay()must be called when the clock reaches midnight
Understanding the Question
You are not writing a whole program or a loop. The procedure Tick() is already said to be called every second by something else.
So your job is to write the code that happens inside one call:
- increase the seconds
- if seconds go past 59, reset them and increase minutes
- at that moment, call
CheckAlarm()because a new minute has started - if minutes go past 59, reset them and increase hours
- if hours go past 23, reset them to 0 and call
NewDay()because the time has become midnight
The key phrase is "as appropriate". That means MM and HH do not change on every call, only when the lower unit rolls over.
Approach
A clean way to solve this is to treat the clock like an odometer:
- always update the smallest unit first:
SS - only when
SSreaches 60 do you reset it and updateMM - only when
MMreaches 60 do you reset it and updateHH - only when
HHreaches 24 do you reset it to 0
Then place the procedure calls at the correct trigger points:
CALL CheckAlarm()immediately after the minute changesCALL NewDay()immediately after the hour changes from 23 to 0
Because the variables are global, no local declarations are needed here.
Step-by-Step Reasoning
Start with the basic action:
SS ← SS + 1
Every call to Tick() means one second has passed, so this must always happen.
Next, check whether seconds have overflowed:
IF SS = 60 THEN
Why 60? Because valid seconds are only 0 to 59. If the old value was 59 and we added 1, the new value becomes 60, which is invalid and signals rollover.
So inside that IF:
SS ← 0
MM ← MM + 1
This resets seconds and advances the minute.
Now the question says CheckAlarm() must be called at the start of each minute. The start of a minute is exactly when seconds have reset to 0 and the minute has just increased. So this is the correct place:
CALL CheckAlarm()
Now check whether the minute has overflowed:
IF MM = 60 THEN
Again, minutes are only valid from 0 to 59. So if incrementing minutes produces 60, that means a new hour has started.
Inside that IF:
MM ← 0
HH ← HH + 1
That resets minutes and increases hours.
Now handle the hour overflow:
IF HH = 24 THEN
A 24-hour clock stores hours from 00 to 23. So after 23:59:59, one more tick makes the internal hour value become 24 before reset, and that is the signal that midnight has been reached.
So then:
HH ← 0
CALL NewDay()
This changes the time to 00:00:00 and triggers whatever start-of-new-day processing the program needs.
Putting it all together gives:
PROCEDURE Tick()
SS ← SS + 1
IF SS = 60 THEN
SS ← 0
MM ← MM + 1
CALL CheckAlarm()
IF MM = 60 THEN
MM ← 0
HH ← HH + 1
IF HH = 24 THEN
HH ← 0
CALL NewDay()
ENDIF
ENDIF
ENDIF
ENDPROCEDURE
You can test the logic with a few values:
16:30:10becomes16:30:11— only seconds change16:30:59becomes16:31:00— seconds reset, minute increases,CheckAlarm()is called16:59:59becomes17:00:00— seconds and minutes reset, hour increases,CheckAlarm()is called23:59:59becomes00:00:00— all three roll over,CheckAlarm()andNewDay()are called
Key Takeaways
- Time update problems are solved as cascading rollovers from the smallest unit upward.
- Use nested IF statements so larger units are only updated when necessary.
- In 24-hour time, valid ranges are
SS: 0..59,MM: 0..59,HH: 0..23. - Event procedures such as
CheckAlarm()andNewDay()must be placed exactly where their trigger condition occurs.
Common Mistakes
- Incrementing
MMevery timeTick()is called: a tick is one second, not one minute. - Forgetting to reset
SSto 0 when it reaches 60: this leaves an invalid time value. - Calling
CheckAlarm()on every tick: it should only happen when a new minute starts. - Using
HH = 60instead ofHH = 24: hours in 24-hour notation do not go to 59. - Forgetting to reset
MMto 0 when it reaches 60: this breaks the hour rollover. - Calling
NewDay()without resettingHHto 0: midnight must be represented as00:00:00, not24:00:00. - Using assignment incorrectly: in CIE pseudocode, assignment must use
←, not=.
Things to Be Careful About
Tick()is already called every second, so do not write an extra loop inside it.- The comparisons should be against the overflow values:
SS = 60,MM = 60,HH = 24. CheckAlarm()should be inside theSS = 60block, because that is when a new minute begins.NewDay()should only be called when the clock actually rolls from hour 23 to hour 0.- Because the variables are global, you do not need to redeclare them locally.
- Make sure all
IFblocks are properly closed withENDIFand the procedure ends withENDPROCEDURE.
An algorithm will output the last three lines from a text file Result.txt
The lines need to be output in the same order as they appear in the file.
Assume:
• Three variables LineX, LineY and LineZ will store the three lines. These are of type string and all three variables have been initialised to an empty string.
• The file exists and contains at least three lines.
The algorithm to output the lines is expressed in eight steps.
Complete the steps.
- Open the file ...........................................................................
- Loop until ...........................................................................
- ........................................................................... and store in
ThisLine - Assign
LineYtoLineX - Assign
LineZtoLineY - Assign
ThisLinetoLineZ - After the loop, ...........................................................................
- Output
LineX,LineY,LineZ
Answer
- Open the file
Result.txtfor reading - Loop until end of file
- Read a line from the file and store in
ThisLine - Assign
LineYtoLineX - Assign
LineZtoLineY - Assign
ThisLinetoLineZ - After the loop, close the file
- Output
LineX,LineY,LineZ
Open Result.txt for reading; loop until end of file; read a line into ThisLine; after the loop close the file; output LineX, LineY, LineZ.
Background Concept
When a text file is processed sequentially, the program reads one line at a time from the start of the file to the end. The usual pattern is:
- open the file for reading
- repeat until end of file
- read the next line
- process it
- close the file
This question uses a common technique for keeping the last few items seen so far. Instead of storing the whole file, the algorithm keeps only three variables. Each time a new line is read, the older saved lines are shifted along:
- the previous middle line becomes the oldest
- the previous newest line becomes the middle
- the line just read becomes the newest
After the whole file has been read, those three variables hold the last three lines in the same order they appeared in the file.
Understanding the Question
You are given an incomplete eight-step algorithm. The file is called Result.txt, and it is guaranteed to contain at least three lines. The variables LineX, LineY and LineZ already exist and start as empty strings.
You need to complete the missing steps so that, after reading the whole file, the algorithm outputs the final three lines in the correct order.
The important clue is the word last. Because the algorithm needs the final three lines, it cannot stop early. It must read all the way to the end of the file.
Approach
The correct strategy is a one-pass scan through the file:
- Open
Result.txtfor input. - Keep reading until there are no more lines.
- For each new line, shift the stored values along.
- Put the newly read line into the newest variable.
- When the loop finishes, close the file.
- Output the three saved lines.
This avoids needing an array or storing the entire file.
Step-by-Step Reasoning
Step 1 must open the file Result.txt for reading, because the algorithm needs to get data from the file.
Step 2 must loop until the end of the file. That is the normal condition for sequential file reading, and it is essential here because the last three lines are not known until the file has been fully read.
Step 3 must read the next line and place it into ThisLine. That gives the algorithm the current line to process.
Steps 4, 5 and 6 perform the rolling update:
LineYis copied toLineXLineZis copied toLineYThisLineis copied toLineZ
Suppose the current stored values are the three most recent lines seen so far. When a new line arrives, the oldest one is discarded, the other two move one position left, and the new line becomes the newest.
For example, if the variables currently hold:
LineX = line 4LineY = line 5LineZ = line 6
and the next line read is line 7, then after steps 4 to 6 they become:
LineX = line 5LineY = line 6LineZ = line 7
So the variables still hold the latest three lines in the correct order.
Step 7 closes the file after the reading loop ends. This is standard file-handling practice.
Step 8 outputs LineX, LineY and LineZ. Because of the shifting process, these are now the last three lines from the file in the same order as in the file.
Key Takeaways
- To get the last few records from a sequential file, read the whole file.
- A rolling set of variables can store the most recent values without needing an array.
- End-of-file is the standard loop condition for reading all lines of a text file.
- The order of the shift matters if you want to preserve the original sequence.
Common Mistakes
- Stopping after reading only three lines. That would give the first three lines, not the last three.
- Outputting
LineZ,LineY,LineX. That would reverse the required order. - Forgetting to close the file after the loop.
- Using the wrong shift direction, such as assigning
LineXtoLineY. That would overwrite useful data and break the rolling update. - Reading outside the loop or after end of file.
Things to Be Careful About
- The loop must continue until end of file, not until three lines have been read.
- The assignments in steps 4 to 6 must happen in the given order so earlier values are preserved correctly.
ThisLineshould store the line just read before it is copied intoLineZ.- The question states the file has at least three lines, so you do not need extra handling for shorter files in this part.
Answer
- Steps 4, 5 and 6 shift the previously read lines along so that
LineX,LineYandLineZalways contain the most recent three lines read, in the correct order.
They shift the stored lines so LineX, LineY and LineZ always hold the latest three lines in the correct order.
Background Concept
A rolling update is used when a program only needs the most recent few values from a sequence. Instead of storing everything read so far, the algorithm keeps a small fixed number of variables and updates them each time a new value arrives.
In this question, there are three variables for three lines:
- oldest of the current three
- middle of the current three
- newest of the current three
Whenever a new line is read, the old values must be shifted to make space for it.
Understanding the Question
This part does not ask you to rewrite the algorithm. It asks for the purpose of steps 4, 5 and 6.
So you should explain what those assignments achieve overall, not just repeat them line by line.
Approach
Look at what each assignment does to the stored lines:
LineYmoves intoLineXLineZmoves intoLineY- the newly read line moves into
LineZ
That tells you these steps are maintaining a sliding set of the three most recent lines.
Step-by-Step Reasoning
Before a new line is read, suppose the variables already store the last three lines seen so far.
When another line is read:
- The value in
LineYis copied intoLineX, so the previous middle line becomes the oldest stored line. - The value in
LineZis copied intoLineY, so the previous newest line becomes the middle stored line. - The new line in
ThisLineis copied intoLineZ, so it becomes the newest stored line.
This means the previous oldest line is discarded, and the variables now hold the newest set of three lines. That is exactly what is needed if the final aim is to output the last three lines in the file.
Key Takeaways
- A rolling set of variables can keep track of the most recent items.
- Shifting values is a way to discard the oldest item and include a new one.
- The overall purpose is more important here than repeating the assignments.
Common Mistakes
- Saying the steps sort the lines. They do not sort anything.
- Saying they read the file. The reading happens in step 3, not steps 4 to 6.
- Saying they reverse the lines. They preserve the original order of the most recent three lines.
Things to Be Careful About
- Mention both ideas: keeping the last three lines and keeping them in the correct order.
- Do not describe them as storing the first three lines.
- Keep the answer focused on purpose, since this is only a one-mark explanation question.
The requirement changes, and the algorithm will now output three lines from the file, starting from a given line number.
The modified algorithm will be implemented as a function which will:
• be called with an integer parameter representing the given line number
• output three lines, starting at the given line
• return TRUE if the 3 lines are output, or FALSE if it was not possible to output the 3 lines.
Describe the changes that need to be made to steps 2 to 8 of the algorithm given in part (a).
Answer
- Add a counter for the current line number and a counter for how many lines have been output.
- Change step 2 so the loop continues while not end of file and fewer than 3 required lines have been output.
- After reading each line in step 3, increment the line-number counter; if the counter is the given line number, the given line number + 1 or the given line number + 2, output
ThisLineand increment the output counter. - Remove steps 4, 5 and 6. After the loop, close the file and
RETURN TRUEif 3 lines were output; otherwiseRETURN FALSE.
Use a line counter and an output counter; read until EOF or until 3 lines have been output; output only lines from the given line number to the next two; remove the shifting steps; close the file and return TRUE if 3 lines were output, otherwise FALSE.
Background Concept
A function is used when an algorithm must produce a result back to the caller. In this case, the result is Boolean:
TRUEmeans the function successfully output three linesFALSEmeans it could not, usually because the file did not contain enough lines from the requested starting point onward
When reading from a text file starting at a given line number, the algorithm usually needs:
- a counter for the current line number being read
- logic to decide whether the current line is one of the required lines
- a way to stop once enough lines have been output or when the file ends
This is different from the original problem. The original problem needed the last three lines, so it had to keep shifting values and read the entire file. The new problem needs three lines starting at a known line number, so the algorithm can use counting instead.
Understanding the Question
The original steps 2 to 8 were designed for a different requirement. Now the function is called with a given starting line number, and it must:
- output exactly three lines starting from that line
- return
TRUEif that succeeds - return
FALSEif there are not enough lines available
The question asks you to describe changes to steps 2 to 8, not rewrite step 1. So the file is still opened in the same way, but the loop and processing logic must change.
Approach
The best approach is:
- Count lines as they are read.
- When the current line number reaches the given line number, start outputting lines.
- Keep outputting until three lines have been output.
- If end of file is reached before three lines are output, return
FALSE. - Otherwise return
TRUE.
Because the required lines are known by position, the shifting variables LineX, LineY and LineZ are no longer needed for this version.
Step-by-Step Reasoning
In the original version, step 2 was simply to loop until end of file. That made sense because the algorithm needed the final three lines.
In the new version, the loop should still check for end of file, but it can also stop early once three lines have been output. So the loop condition becomes effectively:
- not end of file
- and fewer than three required lines output so far
Next, after each line is read, the algorithm must know which line number it is currently processing. So a current-line counter is needed and must be incremented for each line read.
Then the algorithm compares the current line number with the given starting line number:
- if current line number is less than the given line number, ignore the line
- if current line number is the given line number, output it
- also output the next two lines after that
Each time one of these required lines is output, the algorithm should increase a second counter that records how many lines have been output.
Once that counter reaches 3, the function has done its job.
This means steps 4, 5 and 6 from part (a) are no longer appropriate. Those steps were only for keeping the last three lines seen in the file. Here, we are not interested in the last three lines at all; we are interested in three specific lines based on position.
After the loop, the file should still be closed.
Finally, the function must return a Boolean result:
- if the number of lines output is 3, return
TRUE - otherwise return
FALSE
For example, if the caller requests line 10:
- line 10 is output when reached
- line 11 is output next
- line 12 is output next
- if all three are found, return
TRUE - if the file ends at line 11, only two lines can be output, so return
FALSE
Key Takeaways
- Changing the requirement often means changing the whole algorithm strategy, not just a small detail.
- For line-position problems, counters are more useful than rolling storage variables.
- A function should return a value that tells the caller whether it succeeded.
- Combining loop conditions can make file processing more efficient by stopping as soon as enough data has been found.
Common Mistakes
- Keeping steps 4, 5 and 6 unchanged. Those are only for finding the last three lines.
- Forgetting to count line numbers. Without that, the algorithm cannot tell when to start outputting.
- Returning
TRUEjust because the starting line exists, even if fewer than three lines were available. - Looping only until the given line number is reached, instead of continuing until all three lines are output.
- Forgetting to close the file before returning.
Things to Be Careful About
- The function must output three lines starting from the given line, not just the one matching the given line number.
- You need two separate counts: current line number and number of required lines output.
- If the file ends before three lines are output, the correct return value is
FALSE. - Since the question asks for changes to steps 2 to 8, step 1 remains the file-open step from part (a).
- Be clear that the output may now happen during the loop rather than only after it.
A program includes the following assignment statement:
Result ← STR_TO_NUM(x) / STR_TO_NUM(y)
When the program evaluates the expression in the statement, it performs a calculation.
Variable Result is of type real and variables x and y are of type string.
Two checks are required before the calculation is performed:
- The two strings represent valid numeric values.
- The numeric value of string
yis not zero.
Identify the type of error that could occur if these checks are not carried out and state a cause of this error.
Type ..........................................................................................................................................
Cause .......................................................................................................................................
Answer
- Type: run-time error
- Cause:
xorymay not contain a valid numeric value whenSTR_TO_NUM()is used, or the numeric value ofymay be0, causing division by zero.
Type: run-time error; Cause: invalid numeric string conversion or division by zero.
Background Concept
A run-time error is an error that happens while a program is executing. The program may have been written with correct syntax, but when it actually runs it attempts an invalid operation.
Typical examples include:
- dividing by zero
- reading a file that does not exist
- using an array index outside its valid range
- converting data into an invalid type
That is different from:
- a syntax error, which stops the program from being translated correctly
- a logic error, where the program runs but produces the wrong result
In this question, x and y are strings, so before arithmetic can happen they must first be converted into numbers.
Understanding the Question
The statement is:
Result ← STR_TO_NUM(x) / STR_TO_NUM(y)
So the program is doing two conversions from string to number, then a division. The question says two checks are needed first:
- both strings must represent valid numbers
- the numeric value of
ymust not be zero
If those checks are skipped, the program may fail during execution. The task is to name the type of error and give a cause.
Approach
First, decide whether the failure would happen before running or while running. Here, the statement itself is syntactically valid, so the problem appears only when the program tries to execute it with unsuitable data.
Then give one valid cause from the information in the stem:
- invalid numeric conversion, or
- division by zero
Either is enough as the cause.
Step-by-Step Reasoning
STR_TO_NUM(x) means "convert the string in x into a number".
If x contains something like "abc" or any non-numeric text, that conversion cannot be completed properly. The program reaches that line, tries to convert the value, and fails there. That is a run-time error.
Similarly, even if both strings are valid numeric strings, there is still the division:
number / STR_TO_NUM(y)
If STR_TO_NUM(y) gives 0, the program attempts division by zero. That is also a run-time error because it happens when the program is executing the statement.
So the correct error type is run-time error, and a correct cause is either invalid numeric conversion or division by zero.
Key Takeaways
- Run-time errors happen during execution, not during translation.
- Converting a string to a number is only safe if the string is a valid numeric value.
- Division must always be protected against a zero divisor.
Common Mistakes
- Writing "logic error": that would mean the program runs but gives a wrong answer, not that it crashes on invalid data.
- Writing "syntax error": the statement is syntactically valid, so translation is not the issue.
- Giving a vague cause such as "bad data" without saying why it causes failure.
Things to Be Careful About
- The cause must match the statement given. The risky operations here are
STR_TO_NUM()and division. - The question asks for a type of error and a cause, so both parts are needed.
- If you give the cause as division by zero, make it clear that it is the numeric value of
ythat becomes zero after conversion.
The designer considers implementing the checks and calculation as a module (a procedure or a function). One reason for this is that the same checks and calculations are performed at several places in the program.
Give another reason why this is a suitable approach and state what is avoided by this approach.
Reason .....................................................................................................................................
Avoided .....................................................................................................................................
Answer
- Reason: the checks and calculation can be tested/debugged and maintained more easily if they are placed in one module.
- Avoided: duplicated / repeated code.
Reason: easier testing/debugging/maintenance in one module; Avoided: duplicated code.
Background Concept
A module is a self-contained section of a program that performs one task. In this syllabus, that usually means a procedure or a function.
Using modules is part of decomposition: breaking a larger problem into smaller manageable parts. Good modular design gives several benefits, such as:
- easier testing
- easier debugging
- easier maintenance
- clearer program structure
- less duplicated code
A function is especially suitable when a task produces and returns a value.
Understanding the Question
The question already gives one reason for using a module: the same checks and calculation happen in several places in the program. That is the reuse reason.
You are asked for another reason, so you should not just repeat "it can be reused". You also need to say what is avoided by using the module approach.
Approach
Choose one standard advantage of modular programming other than reuse. The safest choices are:
- easier to test
- easier to debug
- easier to maintain
- easier to understand
Then state what is avoided. Since one module replaces multiple copied sections, what is avoided is duplicated or repeated code.
Step-by-Step Reasoning
If the checking and calculation code is placed in one module, the programmer can focus on that one section separately.
For example:
- it can be tested independently with different values of
xandy - if there is a bug, the programmer knows exactly where to look
- if the rule changes later, only one module needs to be edited
That is why "easier testing/debugging/maintenance" is a valid reason.
What does this approach avoid? Without a module, the programmer may write the same checking and calculation statements in several places. That leads to repeated code. So the avoided issue is code duplication.
Key Takeaways
- Modules help organise programs into clear, separate tasks.
- A good reason for using a module is often easier testing, debugging, or maintenance.
- One major thing avoided by modules is duplicated code.
Common Mistakes
- Repeating the reason already given in the question, such as "because it is used several times".
- Giving something vague like "it is better" without saying why.
- Saying "errors are avoided" as the avoided point. That is too general; the expected idea is duplicated code.
Things to Be Careful About
- The question asks for another reason, so do not restate reuse.
- Make sure the two parts are distinct: one is a benefit, the other is what is avoided.
- Keep the answer tied to modules, not to general programming quality with no explanation.
The module to perform the checks and calculation will be implemented as a function. The function will need to return both a real and a Boolean value. To achieve this a record type is defined in pseudocode as follows:
TYPE Result
DECLARE Done : BOOLEAN
DECLARE Value : REAL
ENDTYPE
The function Evaluate() will:
• take two parameters of type string representing the two numeric values
• return a variable of type Result with the Done field set to FALSE if either of the following applies:
◦ at least one of the strings does not represent a valid numeric value
◦ the numeric value of the string representing value y is zero
• otherwise return a variable of type Result with the Done field set to TRUE and the Value field assigned the result of the formula (based on the numeric value of the two parameters).
Write pseudocode for the function Evaluate().
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
Answer
FUNCTION Evaluate(BYVAL x : STRING, BYVAL y : STRING) RETURNS Result
DECLARE TempResult : Result
DECLARE NumX, NumY : REAL
TempResult.Done ← FALSE
IF IsNumeric(x) AND IsNumeric(y) THEN
NumX ← STR_TO_NUM(x)
NumY ← STR_TO_NUM(y)
IF NumY <> 0 THEN
TempResult.Done ← TRUE
TempResult.Value ← NumX / NumY
ENDIF
ENDIF
RETURN TempResult
ENDFUNCTION
See completed pseudocode
Background Concept
A function is used when a module must return a value. Normally a function returns one value, but sometimes a task naturally produces more than one result. In this question, the function must return:
- whether the calculation was completed successfully
- the calculated real value if it was successful
A common way to return both pieces of information is to define a record type. A record groups related fields together under one data type.
Here the record type is:
TYPE Result
DECLARE Done : BOOLEAN
DECLARE Value : REAL
ENDTYPE
So the function can return one Result record containing both fields.
The logic also needs validation before calculation:
- check both strings are valid numeric values
- check the divisor is not zero
- only then perform the division
Understanding the Question
You must write the function Evaluate().
It takes two string parameters, representing the values to be used in the calculation. The function must return a variable of type Result.
The rules are precise:
- if either string is not numeric, return a
Resultrecord withDone ← FALSE - if the numeric value of
yis zero, return aResultrecord withDone ← FALSE - otherwise set
Done ← TRUEand setValueto the result of:
STR_TO_NUM(x) / STR_TO_NUM(y)
So this is a selection problem: do the calculation only if both checks pass.
Approach
A neat structure is:
- declare a local variable of type
Result - assume failure first by setting
Done ← FALSE - test whether both strings are numeric
- convert them to real values only after that check passes
- test whether the second numeric value is not zero
- if safe, perform the division and set
Done ← TRUE - return the record
Setting Done to FALSE at the start is useful because then every invalid case is already handled. You only change it to TRUE in the success case.
Step-by-Step Reasoning
Start the function header:
FUNCTION Evaluate(BYVAL x : STRING, BYVAL y : STRING) RETURNS Result
This states the function name, its two string parameters, and its return type.
Next, declare the local variables:
DECLARE TempResult : Result
DECLARE NumX, NumY : REAL
TempResult is the record that will eventually be returned. NumX and NumY store the converted numeric values.
Now initialise the Boolean field:
TempResult.Done ← FALSE
This is a good default. If anything goes wrong, the returned record already shows that the calculation was not completed.
Next, validate the inputs:
IF IsNumeric(x) AND IsNumeric(y) THEN
The important idea is that both strings must be valid numbers before STR_TO_NUM() is used. The exact validation function name may vary, but the logic must clearly test that both values are numeric.
Only after that should conversion happen:
NumX ← STR_TO_NUM(x)
NumY ← STR_TO_NUM(y)
This order matters. If you convert before checking, you are back to risking the run-time error from part (a).
Then check the divisor:
IF NumY <> 0 THEN
This prevents division by zero.
If the divisor is safe, perform the calculation and mark success:
TempResult.Done ← TRUE
TempResult.Value ← NumX / NumY
Notice that Value only needs to be assigned in the valid case. The question only requires Done ← FALSE when the calculation cannot be completed.
Finally, return the record:
RETURN TempResult
ENDFUNCTION
That completes the function.
Key Takeaways
- A record is useful when a function needs to return more than one related value.
- Validation should happen before conversion and before arithmetic.
- A safe pattern is to assume failure first, then change the success flag only when all checks pass.
- Nested
IFstatements are often the clearest way to protect a calculation step.
Common Mistakes
- Performing
STR_TO_NUM()before checking that the string is numeric. - Forgetting to test whether
yis zero before dividing. - Returning a real value directly instead of returning a
Resultrecord. - Not setting
Donecorrectly for the failure case. - Writing a procedure instead of a function, even though the question explicitly asks for a function.
- Using
=for assignment instead of the pseudocode assignment arrow←.
Things to Be Careful About
- The return type must be
Result, notREALorBOOLEAN. - The function needs two parameters, both of type
STRING. - Use CIE-style pseudocode:
FUNCTION,DECLARE,IF,ENDIF,RETURN,ENDFUNCTION. - Make sure the division only happens in the success branch.
- The exact name of the numeric-validation routine can vary, but the check itself must be present clearly in the logic.
A software developer follows a program development life cycle. The life cycle divides the development process into various stages.
The following table lists some development activities.
Complete the table by writing the name of the life cycle stage for each activity.
| Activity | Name of life cycle stage |
|---|---|
| The walkthrough method is used. | |
| An algorithm is implemented in a programming language. | |
| The client is interviewed about problems with the current system. | |
| The program is modified to run on new hardware. | |
| Records and file structures are defined. |
Answer
- The walkthrough method is used. — Testing
- An algorithm is implemented in a programming language. — Coding
- The client is interviewed about problems with the current system. — Analysis
- The program is modified to run on new hardware. — Maintenance
- Records and file structures are defined. — Design
Testing; Coding; Analysis; Maintenance; Design
Background Concept
The program development life cycle breaks software creation into stages so that the work is organised and checked systematically. Common stages include:
- Analysis: finding out what the user needs and what problems exist with the current system.
- Design: planning the solution, including algorithms, data structures, records and file layouts.
- Coding: translating the design into a programming language.
- Testing: checking that the system works correctly and finding errors.
- Maintenance: changing the program after release, for example to fix faults or adapt it to new conditions.
A question like this tests whether you can recognise a stage from a short description of an activity.
Understanding the Question
You are given five separate development activities and must name the life cycle stage that each one belongs to. The key is to focus on what the activity is trying to achieve:
- gathering requirements points to analysis
- planning structures points to design
- writing code points to coding
- checking correctness points to testing
- changing an existing system after delivery points to maintenance
Approach
For each row, ask: "What is the developer doing here?"
- If they are talking to the client to find out needs, that is analysis.
- If they are deciding how data will be stored or how the program will be structured, that is design.
- If they are writing the actual program, that is coding.
- If they are using a method to check for errors, that is testing.
- If they are altering an existing system to cope with change, that is maintenance.
Step-by-Step Reasoning
-
The walkthrough method is used.
A walkthrough is a way of checking a proposed or written solution step by step to detect errors. That belongs to testing. -
An algorithm is implemented in a programming language.
"Implemented" means turned into actual program statements. That is the coding stage. -
The client is interviewed about problems with the current system.
Interviewing the client is done to gather requirements and understand what is wrong now. That is analysis. -
The program is modified to run on new hardware.
This means an existing program is being changed after it has already been developed. That is maintenance. -
Records and file structures are defined.
Deciding what records exist and how files are organised is part of planning the solution before full coding starts. That is design.
Key Takeaways
- Analysis is about understanding the problem and requirements.
- Design is about planning data, structures and algorithms.
- Coding is writing the program.
- Testing is checking that it works correctly.
- Maintenance is modifying software after release.
Common Mistakes
- Writing design instead of analysis for interviewing the client. Interviewing is about gathering requirements, not planning the solution.
- Writing implementation instead of coding for translating an algorithm into a programming language. In this context, the activity is specifically writing code.
- Writing testing instead of maintenance for modifying software to run on new hardware. The key clue is that the software already exists and is being adapted.
- Confusing records and file structures with coding. These are design decisions made before or alongside coding.
Things to Be Careful About
- Use standard stage names exactly: Analysis, Design, Coding, Testing, Maintenance.
- Read the activity carefully for clue words such as interviewed, implemented, modified, and defined.
- Do not choose a stage based on one familiar keyword alone; think about the purpose of the activity.
The program contains a validation function.
The function will:
• take an integer value as a parameter
• return TRUE if the value is within the range 24 to 37, inclusive
• otherwise return FALSE.
Complete the table to define a test plan to thoroughly test the operation of the function.
| Type of test data | Test data value | Expected result |
|---|---|---|
| Normal | 30 | TRUE |
Answer
| Type of test data | Test data value | Expected result |
|---|---|---|
| Normal | 30 | TRUE |
| Boundary | 24 | TRUE |
| Boundary | 37 | TRUE |
| Abnormal | 23 | FALSE |
| Abnormal | 38 | FALSE |
See explanation
Background Concept
A test plan is a set of test data chosen to check that a module works correctly. For validation questions, the most useful test types are:
- Normal data: valid data that should be accepted.
- Boundary data: values at the edge of the allowed range.
- Abnormal (or invalid/erroneous) data: data that should be rejected.
When a range is inclusive, the end values themselves are allowed. So for a range 24 to 37 inclusive:
- 24 is valid
- 37 is valid
- values below 24 are invalid
- values above 37 are invalid
Understanding the Question
The function takes an integer and returns:
TRUEif the value is between 24 and 37 inclusiveFALSEotherwise
One normal test, 30 → TRUE, is already given. To test thoroughly, you should add tests that prove:
- the lower boundary is accepted
- the upper boundary is accepted
- a value just below the range is rejected
- a value just above the range is rejected
Approach
Start by identifying the two critical limits: 24 and 37. Because the range is inclusive, both should return TRUE. Then pick the nearest invalid values outside the range: 23 and 38. These should return FALSE.
This gives very strong evidence that the comparison logic is correct on both edges of the allowed range.
Step-by-Step Reasoning
-
The valid range is from 24 to 37 inclusive.
That means the function behaves like:- return
TRUEifvalue >= 24 AND value <= 37 - otherwise return
FALSE
- return
-
A normal value already given is 30.
This is comfortably inside the range, soTRUEis correct. -
Test the lower boundary.
- Input 24
- Since 24 is included in the valid range, expected result is TRUE.
-
Test the upper boundary.
- Input 37
- Since 37 is included in the valid range, expected result is TRUE.
-
Test just below the lower boundary.
- Input 23
- 23 is outside the valid range, so expected result is FALSE.
-
Test just above the upper boundary.
- Input 38
- 38 is outside the valid range, so expected result is FALSE.
These four extra tests check both edges and both rejection cases.
Key Takeaways
- For range validation, always test the values on the limits and just outside the limits.
- The word inclusive means the end values are valid.
- A thorough test plan should include a mix of normal, boundary, and abnormal data.
Common Mistakes
- Using 24 or 37 as invalid values. They are valid because the range is inclusive.
- Forgetting to test both ends of the range.
- Giving the wrong expected result for 23 or 38. Both are outside the valid range, so both should return
FALSE. - Choosing more normal data instead of testing the boundaries. Extra normal values do not check the edge logic properly.
Things to Be Careful About
- Read inclusive carefully. If the question had said exclusive, 24 and 37 would not be valid.
- Keep the test data as integers, because the function takes an integer parameter.
- The type labels can vary a little by centre or mark scheme wording, but the important thing is that the chosen values thoroughly test inside, at, and outside the range.
The function is to be tested on its own. When it is shown to work correctly the function will be combined with other modules and testing will continue.
Identify the type of testing that this represents.
Answer
- Unit testing
Unit testing
Background Concept
Unit testing means testing one module, procedure or function on its own, separate from the rest of the system. The purpose is to check that this single unit works correctly before it is joined to other parts.
After individual units work correctly, they are often combined and checked together in integration testing. So unit testing comes first, then broader combined testing follows.
Understanding the Question
The question says the validation function is tested on its own. That is the key clue. A function tested independently is a unit being tested.
The statement about then combining it with other modules explains what happens next, but the testing of the function by itself is the testing type being identified here.
Approach
Look for phrases that indicate scope:
- on its own → unit testing
- combined with other modules → integration testing later
Because the question focuses on the function first being checked separately, the correct answer is unit testing.
Step-by-Step Reasoning
- A function is a single module or small component of the overall program.
- The question says it is tested on its own.
- Testing one component independently is called unit testing.
- Only after that will it be combined with other modules for further testing.
So the identified testing type is unit testing.
Key Takeaways
- Unit testing checks one module in isolation.
- Integration testing checks modules after they have been combined.
- In exam questions, wording such as on its own is a strong clue for unit testing.
Common Mistakes
- Answering integration testing because the question mentions combining modules later. That is the next stage, not the part being identified.
- Giving a vague answer like testing. The question wants the specific testing type.
- Confusing unit testing with white-box or black-box testing. Those describe approaches, not the scope of the component being tested.
Things to Be Careful About
- Read exactly what is happening first. If the module is tested separately, that is unit testing.
- Do not be distracted by what happens afterwards unless the question explicitly asks for the later testing stage.
- Use the exact term unit testing for full credit.
A factory produces food items. The items must be used within a certain number of days after their production date. The number of days is known as the shelf life. It is different for each type of item but is always a whole number in the range 1 to 21 (inclusive).
The latest date that an item can be used is called the ‘use-by’ date.
A program is needed to produce labels which show the ‘use-by’ date.
Part of the program is a function GetDate() which will:
• take two parameters: a production date and a value representing the shelf life
• return the corresponding ‘use-by’ date.
The program contains a global 1D array DaysInMonth of type integer which stores the number of days in each month (index 1 is January):
| Index | Value |
|---|---|
| 1 | 31 |
| 2 | 28 |
| 3 | 31 |
| 4 | 30 |
| ... | ... |
| 11 | 30 |
| 12 | 31 |
Note: Leap years are not considered
An algorithm uses the array DaysInMonth to calculate a ‘use-by’ date. An alternative design would involve the use of multiple selection statements.
An array-based technique is often used when there is a large number of different values to check and where no pattern exists.
One advantage of using an array-based technique is the speed of execution compared to the use of multiple selection statements.
Give two other advantages of using an array for this type of operation rather than a solution based on multiple selection statements.
1 ................................................................................................................................
2 ................................................................................................................................
Answer
- It is easier to maintain or amend, because a change only needs the value in the array to be updated rather than rewriting several selection statements.
- The code is shorter and simpler to write and read, so there is less repetition and less chance of logic errors.
Easier to maintain and easier to write/read with less repetition.
Background Concept
An array is often used when one input value needs to match one stored result from a list of possible values. This is called a data-driven approach: the data is stored in a structure, and the program uses an index to retrieve the correct value.
Multiple selection statements, such as long nested IF statements or a large CASE structure, can also solve this kind of problem, but they place the values directly inside the program logic. That usually makes the program longer and harder to change.
For month lengths, an array is a natural fit because month number 1 maps to 31, month number 2 maps to 28, and so on.
Understanding the Question
The question already tells you one advantage: using an array can be faster than checking many selection statements. So you must not repeat speed.
You need two other advantages of using DaysInMonth rather than writing a large set of selection statements such as:
- if month = 1 then 31
- else if month = 2 then 28
- and so on.
The best answers focus on software design benefits such as maintenance, clarity, reduced repetition, or reduced error risk.
Approach
Think about what arrays do well here:
- They store related values together.
- They allow the same retrieval method for every case.
- They separate the data from the decision logic.
Then turn those ideas into practical advantages a programmer or maintainer would care about.
Step-by-Step Reasoning
The month number can be used directly as the array index, so one access gives the correct number of days.
Compared with many selection statements:
- You do not need one separate branch for every month.
- The program is therefore shorter and easier to follow.
- If one stored value changes, you edit the array entry rather than changing the logic structure.
- Because there is less repeated code, there is less opportunity to make mistakes such as missing a month or typing the wrong value in one branch.
So two strong credited points are:
- easier to maintain or amend
- shorter, clearer code with less repetition and fewer logic errors
Key Takeaways
- Arrays are useful when a value can be found by position or index.
- A data-driven solution is often easier to maintain than a long selection-based solution.
- Good answers to comparison questions should avoid repeating a point already given in the question.
Common Mistakes
- Repeating "faster" or "better performance" even though the question already gives that advantage.
- Giving vague answers such as "it is better" without saying why.
- Describing how arrays work instead of stating an actual advantage.
Things to Be Careful About
- The question asks for advantages "for this type of operation", so keep your points relevant to lookup/matching tasks.
- Make sure each point is distinct; for example, "shorter code" and "less repetition" are closely related, so phrase them as one clear advantage rather than trying to count them as two separate points unless you clearly separate readability from maintenance.
Complete the pseudocode for the function GetDate().
Date functions from the insert should be used in your solution.
FUNCTION GetDate(ProductionDate : DATE, ShelfLife : INTEGER) RETURNS DATE
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
..........................................................................................................................
ENDFUNCTION
Answer
FUNCTION GetDate(ProductionDate : DATE, ShelfLife : INTEGER) RETURNS DATE
DECLARE DayNumber, MonthNumber, YearNumber : INTEGER
DECLARE UseByDate : DATE
DayNumber ← DAY(ProductionDate) + ShelfLife
MonthNumber ← MONTH(ProductionDate)
YearNumber ← YEAR(ProductionDate)
IF DayNumber > DaysInMonth[MonthNumber] THEN
DayNumber ← DayNumber - DaysInMonth[MonthNumber]
MonthNumber ← MonthNumber + 1
IF MonthNumber > 12 THEN
MonthNumber ← 1
YearNumber ← YearNumber + 1
ENDIF
ENDIF
UseByDate ← SETDATE(DayNumber, MonthNumber, YearNumber)
RETURN UseByDate
ENDFUNCTION
See completed pseudocode
Background Concept
A function is used when a program needs to calculate and return a single result. Here, the result is a DATE, so GetDate() must take the inputs, process them, and return the computed use-by date.
This question combines several Paper 2 ideas:
- using parameters in a function
- extracting parts of a date using built-in date functions
- looking up data in an array
- using selection to deal with overflow into the next month or year
The global array DaysInMonth stores how many days each month contains. Because the month number is already in the range 1 to 12, it can be used directly as the index into the array.
The date functions from the insert are intended to avoid manual string processing. Typical helpers are:
DAY(DateValue)MONTH(DateValue)YEAR(DateValue)SETDATE(Day, Month, Year)
These let you break a date into parts, adjust those parts, then build a new date to return.
Understanding the Question
You must complete the function GetDate(ProductionDate, ShelfLife).
The function must:
- take a production date
- take an integer shelf life
- calculate the corresponding use-by date
- return that new date
You are told to use the date functions from the insert, so the expected method is not string slicing or manual formatting. You should work with day, month and year separately.
The important extra information is that:
- shelf life is always from 1 to 21 inclusive
- leap years are ignored
DaysInMonthis already available globally
That means February is always 28 days, and the array lookup can be trusted.
Approach
The clean approach is:
- Extract the day, month and year from
ProductionDate. - Add
ShelfLifeto the day. - Check whether the new day value is now too large for that month.
- If it is, subtract the number of days in that month and move to the next month.
- If moving past December, wrap the month back to January and add 1 to the year.
- Rebuild the final date and return it.
Because shelf life is at most 21 days, one month adjustment is enough for this question. A more general solution could use a WHILE loop, but an IF is sufficient here and is neatly matched to the given constraints.
Step-by-Step Reasoning
Start by declaring the local variables:
DayNumber,MonthNumber,YearNumberasINTEGERUseByDateasDATE
These hold the date parts while you do the calculation.
Then extract the date parts:
DayNumber ← DAY(ProductionDate) + ShelfLifeMonthNumber ← MONTH(ProductionDate)YearNumber ← YEAR(ProductionDate)
Notice that the shelf life is added immediately to the day number. For example, if the production date is 25 March and the shelf life is 10, then DayNumber becomes 35.
Now check whether that day value still fits in the original month:
IF DayNumber > DaysInMonth[MonthNumber] THEN
This is where the array is used. If MonthNumber is 3, DaysInMonth[3] is 31. If DayNumber is 35, then it is too large for March.
If the day is too large:
- subtract the length of the current month
- move to the next month
So:
DayNumber ← DayNumber - DaysInMonth[MonthNumber]MonthNumber ← MonthNumber + 1
Using the example above, 35 in March becomes 4 in April.
Next, handle the special case where the current month was December. After adding 1, the month would become 13, which is invalid. So you must wrap around:
IF MonthNumber > 12 THENMonthNumber ← 1YearNumber ← YearNumber + 1ENDIF
This changes dates like late December plus shelf life into the correct January date in the next year.
Finally, rebuild the date and return it:
UseByDate ← SETDATE(DayNumber, MonthNumber, YearNumber)RETURN UseByDate
That completes the function.
Key Takeaways
- Use a function when one calculated value needs to be returned.
- Arrays are very useful for month-length lookup because the month number can act as the index.
- Date arithmetic often works by splitting a date into day, month and year, adjusting them, then rebuilding the final date.
- End-of-month and end-of-year cases must always be checked explicitly.
Common Mistakes
- Forgetting to return a
DATEat the end of the function. - Adding the shelf life but not checking whether the day now exceeds the month length.
- Incrementing the month without handling December rolling over to January.
- Using
=instead of the assignment arrow←in pseudocode. - Writing the answer in a real programming language instead of CIE pseudocode.
- Ignoring the given
DaysInMontharray and hard-coding month lengths with manyIFstatements.
Things to Be Careful About
DaysInMonthis indexed from 1 for January, so use the month number directly as given.- Leap years are not considered, so February must remain 28 days.
- Keep the function name and parameter names exactly as shown:
GetDate(ProductionDate : DATE, ShelfLife : INTEGER). - Make sure the year only changes when the month moves past 12.
- The insert's date functions should be used exactly as required by the paper.
- Even though a
WHILEloop is a more general approach, this question's limit of 21 days means a singleIFadjustment is sufficient and efficient.
A program contains six modules with headers as follows:
| Pseudocode module header |
|---|
PROCEDURE Connect() |
FUNCTION Activate(H1 : STRING, Code : INTEGER) RETURNS BOOLEAN |
FUNCTION Sync(T1 : BOOLEAN, S2 : REAL) RETURNS INTEGER |
PROCEDURE Initialise(BYREF ID : INTEGER, BYVAL CC : INTEGER) |
FUNCTION Reset(RA : STRING) RETURNS INTEGER |
FUNCTION Enable(SA : INTEGER) RETURNS BOOLEAN |
Module Connect() will call either Activate() or Sync(). This is decided at run-time.
Answer
See structure chart
Background Concept
A structure chart shows the modular structure of a program. Each rectangle is a module, and the lines show which module calls which other module.
Important ideas used here are:
- A procedure performs a task but does not return a function value.
- A function returns a value.
- A data couple shows data being passed between modules.
- A control couple shows a control flag or decision value being passed.
- A selection symbol shows that one of several modules is chosen at run-time.
- An iteration symbol shows that one or more module calls are repeated.
- A BYVAL parameter is passed in only.
- A BYREF parameter can be changed by the called module and the changed value is available to the caller.
So, when reading module headers, you should look for:
- the module names
- whether they are procedures or functions
- their parameters
- whether a value is returned
- whether any parameter is BYREF
Understanding the Question
You are given six module headers and an incomplete structure chart. You must fill in the missing module names and the missing parameter/return labels so that the chart matches the headers.
The key clue is this sentence:
Connect()will call eitherActivate()orSync()at run-time.
That immediately tells you that Connect is the top module and there must be a selection between Activate and Sync underneath it.
You then use the remaining headers to decide:
- which module goes on the left and which goes on the right
- which lower-level modules belong under each one
- which arrows carry parameters downwards
- which arrows carry returned values or BYREF values upwards
Approach
A good method is:
- Put the top-level caller first.
- Use the question statement to place the two alternative modules directly below it.
- Match each incomplete branch to the header that fits its shape.
- Use the parameter names from the headers to label the arrows.
- Use function return types and BYREF parameters to decide which arrows go back up.
- Add the special symbols already implied by the chart: selection at the top and iteration lower down.
The incomplete chart already helps a lot:
- the left middle module has two children, so it must be the module that calls two others
- the right middle module has one child and a two-way connection, which matches a procedure with a BYREF parameter
Step-by-Step Reasoning
Start with the top module:
Connect()is already at the top.
Now use the statement "either Activate() or Sync()":
- directly below
Connect, placeSyncon one branch andActivateon the other - add the selection symbol under
Connectbecause only one of these two is called at run-time
Decide which side is which:
- the left middle box must be
Syncbecause it has two child modules beneath it - the right middle box must be
Activatebecause it has only one child beneath it
Label the Connect to Sync branch:
Sync(T1 : BOOLEAN, S2 : REAL) RETURNS INTEGER- so
T1andS2must go fromConnectdown toSync Syncalso returns an integer back up toConnect
Label the Connect to Activate branch:
Activate(H1 : STRING, Code : INTEGER) RETURNS BOOLEAN- so
H1andCodego fromConnectdown toActivate Activatereturns a Boolean value back up toConnect
Now fill the modules below Sync:
- the two remaining functions that sensibly fit here are
ResetandEnable - so the lower left box is
Reset - the lower middle box is
Enable
Add their parameter/return links:
Reset(RA : STRING) RETURNS INTEGERRAgoes down fromSynctoReset- the integer return value goes back up from
ResettoSync
Enable(SA : INTEGER) RETURNS BOOLEANSAgoes down fromSynctoEnable- the Boolean return value goes back up from
EnabletoSync
The curved loop under Sync means these lower calls are repeated, so keep the iteration symbol across those child calls.
Now fill the module below Activate:
- the only remaining module is
Initialise - so the lower right box is
Initialise
Add its parameter links using the header:
Initialise(BYREF ID : INTEGER, BYVAL CC : INTEGER)CCis BYVAL, so it is passed down fromActivatetoInitialiseIDis BYREF, so it is passed toInitialiseand the updated value is available back inActivate- that is why the chart shows a two-way vertical data flow for
ID
This completes the structure chart consistently with all six headers.
Key Takeaways
- Read structure charts from the top module downwards.
- Use the module header to determine parameters and return values.
- A function returns a value; a procedure does not.
- A BYREF parameter must allow the changed value to be available to the caller.
- Selection means one branch is chosen; iteration means calls are repeated.
Common Mistakes
- Putting
ActivateandSyncunder the wrong parent. They must both be called byConnect. - Forgetting the selection symbol between
SyncandActivate. The question explicitly says either one is called at run-time. - Treating
Initialiselike a function return. It is a procedure, so there is no function result. - Forgetting that
IDisBYREF. That value must be shown as available back to the caller. - Omitting return arrows for functions such as
Reset,Enable,SyncorActivate.
Things to Be Careful About
- Use the module names exactly as given:
Connect,Sync,Activate,Reset,Enable,Initialise. - Match parameter names exactly:
T1,S2,H1,Code,RA,SA,CC,ID. - Do not invent extra modules or extra parameters.
- Keep the hierarchy correct:
ResetandEnableare belowSync;Initialiseis belowActivate. - Distinguish carefully between a returned function value and a changed BYREF parameter.
Answer
- It shows iteration.
- The module calls beneath it are repeated, so the lower module or modules may be called more than once until the required condition is met.
It shows iteration: the lower module calls are repeated.
Background Concept
In a structure chart, special symbols show how modules are called.
One important symbol is the iteration symbol. This means a module call, or a group of module calls, is repeated in a loop rather than being executed just once.
This is different from:
- sequence: modules are called in order once
- selection: one of several alternatives is chosen
So if you see the curved loop-like symbol across module connections, it means repeated execution.
Understanding the Question
The question asks for the meaning of the curved arrow symbol shown in the structure chart from part (a).
In that diagram, the curved symbol appears around the calls from one module to lower modules. You are not being asked to redraw anything, only to explain what that symbol means.
Approach
To answer this, name the symbol first, then explain its effect on program flow.
A complete answer should say:
- it means iteration or repetition
- the module or modules under that symbol are called repeatedly, not just once
Step-by-Step Reasoning
The curved symbol in a structure chart is used to show looping behaviour.
So the first marking point is:
- it represents iteration
Then explain what iteration means in this context:
- the subordinate module calls are repeated
- they may be called several times until some condition causes the loop to stop
That is enough for full credit.
Key Takeaways
- A curved loop symbol in a structure chart means iteration.
- Iteration means one or more lower-level modules are called repeatedly.
- Do not confuse iteration with selection.
Common Mistakes
- Saying it means selection. Selection is choosing one branch from alternatives, not repeating calls.
- Saying it just means "the modules are connected". The question asks for the special meaning of the symbol.
- Describing the order of execution without mentioning repetition.
Things to Be Careful About
- Use the term iteration or repetition explicitly.
- Make clear that the lower module calls can happen more than once.
- Do not mix this up with the decision/selection symbol used where one of two modules is chosen.
An exam paper has a maximum of 75 marks. One of five pass grades (A to E) is assigned, depending on the mark obtained. The lowest mark for a given grade is known as the grade boundary. For example, if the grade boundary for an A grade is 65 marks, then any candidate who achieves a mark of 65 or above will be awarded an A. A grade of U is awarded for marks below the E grade boundary.
The five grade boundaries are stored in a global 1D array GradeBoundary of type integer.
For example:
| Element | Value | Comment |
|---|---|---|
GradeBoundary[1] | 65 | The minimum mark for an A grade. |
GradeBoundary[2] | 57 | The minimum mark for a B grade. |
GradeBoundary[3] | 43 | The minimum mark for a C grade. |
GradeBoundary[4] | 35 | The minimum mark for a D grade. |
GradeBoundary[5] | 27 | The minimum mark for an E grade. |
A global 2D array Result of type integer contains candidate marks for the exam. Each row relates to one candidate. Column 1 contains the candidate mark and column 2 contains the unique candidate ID.
For example, for the fourth and fifth candidates:
| Element | Mark | Element | ID |
|---|---|---|---|
Result[4, 1] | 56 | Result[4, 2] | 1074832 |
Result[5, 1] | 54 | Result[5, 2] | 2573839 |
There are more rows in the array than candidates who sit the exam. Any unused rows will be at the end of the array.
Candidate papers that are given a mark within two marks of any grade boundary must be checked.
For example, given the values in the example grade boundaries above, any paper that was awarded between 41 and 45 marks (inclusive) would need to be checked.
A program is being written to identify papers that need to be checked.
The programmer has defined the first program module as follows:
| Module | Description |
|---|---|
CheckMark() | • called with a parameter of type integer representing a candidate mark • returns TRUE if the mark is within 2 of any of the five grade boundaries, otherwise returns FALSE |
Write pseudocode for module CheckMark().
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
Answer
FUNCTION CheckMark(BYVAL CandidateMark : INTEGER) RETURNS BOOLEAN
DECLARE Boundary : INTEGER
FOR Boundary ← 1 TO 5
IF CandidateMark >= GradeBoundary[Boundary] - 2 AND CandidateMark <= GradeBoundary[Boundary] + 2 THEN
RETURN TRUE
ENDIF
NEXT Boundary
RETURN FALSE
ENDFUNCTION
See completed pseudocode
Background Concept
A function is used when a module must return a single value. Here, CheckMark() returns a Boolean value: TRUE if the mark is close enough to any grade boundary, otherwise FALSE.
The key idea is an inclusive range check. If a boundary is, for example, 43, then marks from 41 to 45 inclusive are within 2 marks of that boundary. In general, for any boundary value B, the valid range is from B - 2 to B + 2.
Because there are exactly five grade boundaries, a simple count-controlled loop is a good solution. Each boundary is stored in the global array GradeBoundary, so the function can inspect each one in turn.
Understanding the Question
The question gives you:
- a global 1D integer array
GradeBoundary[1..5] - one candidate mark passed into the module
- the rule that a paper must be checked if its mark is within 2 marks of any boundary
You are asked to write pseudocode for CheckMark() only. That means this module must:
- take one integer parameter
- compare it with all five boundaries
- return
TRUEif any comparison matches - return
FALSEif none match
It does not need to work out the actual grade letter. It only decides whether the mark is near a boundary.
Approach
The simplest method is:
- Look at each of the five boundaries in turn.
- For each one, test whether the mark is between
boundary - 2andboundary + 2inclusive. - If that condition is true for any boundary, return
TRUEimmediately. - If the loop finishes without finding any match, return
FALSE.
Returning immediately is efficient because once one matching boundary has been found, there is no need to keep checking.
Step-by-Step Reasoning
The function header must show that:
- the module name is
CheckMark - it has one parameter,
CandidateMark, of type integer - it returns a Boolean value
So the header is:
FUNCTION CheckMark(BYVAL CandidateMark : INTEGER) RETURNS BOOLEAN
A loop variable is needed, so we declare Boundary as an integer.
DECLARE Boundary : INTEGER
There are five boundaries, so the loop runs from 1 to 5.
FOR Boundary ← 1 TO 5
Inside the loop, compare the candidate mark with the current boundary. The condition must be inclusive, because marks exactly 2 below or 2 above still count.
IF CandidateMark >= GradeBoundary[Boundary] - 2 AND CandidateMark <= GradeBoundary[Boundary] + 2 THEN
If this is true, the function can immediately return TRUE.
RETURN TRUE
If the loop finishes and no boundary matched, the mark is not within 2 of any boundary, so the function returns FALSE.
RETURN FALSE
This gives the correct Boolean result for every mark.
Key Takeaways
- Use a function when a module must return one value.
- A mark is within 2 of a boundary if it lies in the inclusive range
boundary - 2toboundary + 2. - A count-controlled loop is suitable when the number of items is known in advance.
- Returning as soon as a match is found is a standard and efficient pattern.
Common Mistakes
- Using
ORinstead ofANDin the range check. A value must satisfy both limits, not just one. - Forgetting the range is inclusive. Marks exactly 2 away must still count.
- Checking only one boundary instead of all five.
- Writing a procedure instead of a function, even though the module must return
TRUEorFALSE. - Returning a grade letter such as
AorB, which is not what the module is supposed to do.
Things to Be Careful About
- The array is indexed from
1to5in the question's example, so the loop bounds must match that. GradeBoundaryis global, so it does not need to be passed as a parameter.- Use the assignment arrow
←only for assignment; use comparison operators inside theIFcondition. - Keep the module name and identifier casing exactly as given:
CheckMarkandGradeBoundary.
A second module is defined:
| Module | Description |
|---|---|
CheckAll() | • called with a parameter of type integer representing the number of candidate marks in the Result array• uses CheckMark() to check each candidate mark• for each paper that needs to be checked, write the corresponding candidate ID on a separate line in a new file named GRList.txt• outputs a message with a count of how many papers need to be checked |
Write pseudocode for module CheckAll().
CheckMark() must be used to check each individual mark.
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
...................................................................................................................
Answer
PROCEDURE CheckAll(BYVAL NumberCandidates : INTEGER)
DECLARE Row, Count : INTEGER
Count ← 0
OPENFILE "GRList.txt" FOR WRITE
FOR Row ← 1 TO NumberCandidates
IF CheckMark(Result[Row, 1]) = TRUE THEN
WRITEFILE "GRList.txt", Result[Row, 2]
Count ← Count + 1
ENDIF
NEXT Row
CLOSEFILE "GRList.txt"
OUTPUT Count, " papers need to be checked"
ENDPROCEDURE
See completed pseudocode
Background Concept
A procedure is used when a module performs actions rather than returning a single value. In this question, CheckAll() does several actions:
- processes all candidate records
- uses another module,
CheckMark(), to test each mark - writes certain candidate IDs to a file
- counts how many papers need checking
- outputs a message
The Result array is 2D:
- column 1 stores the mark
- column 2 stores the candidate ID
Because the array has unused rows at the end, the procedure must not scan the whole array blindly. It must only process the number of actual candidates passed in as a parameter.
For file handling, opening a file FOR WRITE creates a new output file for this task. Each time a candidate needs checking, the corresponding ID is written to the file.
Understanding the Question
The question asks for pseudocode for CheckAll().
You are told that this module:
- receives the number of candidate marks in
Result - must use
CheckMark()for each mark - must write each relevant candidate ID to a new file called
GRList.txt - must output how many papers need to be checked
Important clues from the stem:
- only the first
NumberCandidatesrows are valid Result[Row, 1]is the markResult[Row, 2]is the candidate ID- the file required here is a new file, so write mode is appropriate
Approach
The overall pattern is:
- Open
GRList.txtfor writing. - Set a counter to 0.
- Loop through rows
1toNumberCandidates. - For each row, pass the mark from column 1 into
CheckMark(). - If
CheckMark()returnsTRUE, write the ID from column 2 to the file and increase the counter. - After the loop, close the file.
- Output the counter in a message.
This uses modular design correctly because CheckAll() does not repeat the boundary-checking logic; it delegates that task to CheckMark().
Step-by-Step Reasoning
The header must define CheckAll() as a procedure with one integer parameter.
PROCEDURE CheckAll(BYVAL NumberCandidates : INTEGER)
We need:
Rowto move through the arrayCountto record how many papers need checking
So both are declared as integers.
DECLARE Row, Count : INTEGER
The counter starts at 0 because no papers have been identified yet.
Count ← 0
The file must be created as a new file named GRList.txt, so it is opened for writing.
OPENFILE "GRList.txt" FOR WRITE
Now loop through the valid candidate rows only.
FOR Row ← 1 TO NumberCandidates
The mark is stored in column 1, so that is the value passed to CheckMark().
IF CheckMark(Result[Row, 1]) = TRUE THEN
If the function says the paper needs checking, write the candidate ID from column 2 to the file.
WRITEFILE "GRList.txt", Result[Row, 2]
Then increment the count.
Count ← Count + 1
After all rows are processed, close the file.
CLOSEFILE "GRList.txt"
Finally, output a message containing the number of papers needing checks.
OUTPUT Count, " papers need to be checked"
This satisfies every requirement in the question:
- it uses
CheckMark() - it writes only candidate IDs, not marks
- it writes one ID for each flagged paper
- it counts them
- it outputs the count
Key Takeaways
- Use a procedure when a module performs actions rather than returning one value.
- When a 2D array stores different fields in different columns, read the correct column for each task.
- If unused rows exist, loop only over the valid row count provided.
- File handling usually follows the pattern: open, write/read inside a loop, then close.
- A counter accumulator must be initialised before the loop and updated only when the condition is met.
Common Mistakes
- Looping through every row in the array instead of only the first
NumberCandidatesrows. - Writing
Result[Row, 1]to the file instead ofResult[Row, 2], which would write marks instead of IDs. - Rewriting the boundary logic inside
CheckAll()instead of callingCheckMark()as required. - Forgetting to increment the counter when a paper is written to the file.
- Forgetting to close the file after finishing.
- Opening the file for read mode or append mode when this part specifically asks for a new file.
Things to Be Careful About
- Column 1 is the mark; column 2 is the candidate ID. Mixing these up loses marks.
- The parameter is the number of candidates, not the maximum possible size of the array.
Count ← 0must come before the loop.- The
WRITEFILEstatement should be inside theIFblock, not outside it. - Keep the file name exactly as
GRList.txt. - Since this is Paper 2, the answer must be in CIE pseudocode, not a real programming language.
The requirement changes. Instead of a new file, the module described in part (b) needs to add the corresponding candidate ID for each paper that needs to be checked to an existing file.
Explain the change that will need to be made to CheckAll().
Answer
- Open
GRList.txtin append mode instead of write mode, so the new candidate IDs are added to the end of the existing file rather than replacing its current contents.
Open GRList.txt in APPEND mode instead of WRITE mode.
Background Concept
When writing to a text file, the file mode matters:
WRITEcreates a new output file or overwrites the existing contents.APPENDkeeps the existing contents and adds new data at the end.
So the difference is whether old data is preserved.
Understanding the Question
In part (b), the file was supposed to be a new file, so WRITE was correct.
In this part, the requirement changes: the file already exists, and the new candidate IDs must be added to it. That means the old contents must not be lost.
So the question is really asking: what one change is needed so the program adds data to an existing file instead of replacing it?
Approach
The only required change is to alter the file-opening mode.
Where CheckAll() previously opened GRList.txt for writing, it must now open it for appending. The rest of the logic can stay the same.
Step-by-Step Reasoning
In part (b), the file statement would have been something like:
OPENFILE "GRList.txt" FOR WRITE
If that remains unchanged, the existing contents of the file could be overwritten.
To keep the existing file data and add more candidate IDs after it, the open mode must become append mode:
OPENFILE "GRList.txt" FOR APPEND
That is the change the examiner is looking for.
Key Takeaways
WRITEis for creating a new output file or replacing contents.APPENDis for adding data to the end of an existing file.- Small wording changes in a question can require a specific change in file mode.
Common Mistakes
- Saying only that the file should be opened, without stating the new mode.
- Using
WRITE, which would risk deleting existing contents. - Using
READ, which would not allow the program to add data.
Things to Be Careful About
- The key point is not just that the file exists, but that its current contents must be preserved.
- For a 1-mark explain question, the answer must be precise: mention append mode and why it is needed.
- Do not suggest rewriting the whole procedure; only the file-opening method needs to change.

