Syllabus coverage
- Understand what is meant by a programming paradigm
- Show understanding of the characteristics of low-level programming, and write low-level code that uses immediate, direct, indirect, indexed and relative addressing modes
- Show understanding of the characteristics of imperative (procedural) programming, including structural programming (AS content, section 11.3)
- Write imperative (procedural) code that uses variables, constructs, procedures and functions
- Show understanding of the terminology associated with OOP, including objects, properties/attributes, methods, classes, inheritance, polymorphism, containment (aggregation), encapsulation, getters, setters and instances
- Show understanding of how to solve a problem by designing appropriate classes
- Show understanding of, and write code that demonstrates the use of, object-oriented programming
- Show understanding of the characteristics of declarative programming
- Solve a problem by writing appropriate facts and rules based on supplied information
- Write code that can satisfy a goal using facts and rules
- Write code to perform file-processing operations: open a file in read, write and append mode, and close a file
- Read a record from a file and write a record to a file
- Perform file-processing operations on serial, sequential and random files
- Show understanding of an exception and the importance of exception handling, know when it is appropriate, and write program code that uses it
Part of the AS & A Level Computer Science syllabus 9618.
What a paradigm is, and the four in this syllabus
A programming paradigm is a style or approach to writing programs: the set of features a language offers and the way those features are used to break a problem down. It is not the same thing as a language and not the same thing as a program. One language can support more than one paradigm - Python can be written as a procedural program or as an object-oriented one - and the same problem can be solved in any of them.
Paradigms are grouped in two ways. By level, a paradigm is either low-level, where the instructions map closely onto the machine's own operations, or high-level, where the instructions are closer to the problem. By what the programmer writes, a paradigm is either imperative, where the programmer states the steps that must be carried out and the order they go in, or declarative, where the programmer states facts and rules and leaves the order of execution to the system. Low-level, imperative and object-oriented programming are all imperative; declarative programming is not.
The distinction that earns marks is how against what. In an imperative program you write the steps: set a total to zero, add each mark, divide by the count. In a declarative program you write what is true and what must be satisfied, and the system works out how to satisfy it. Object-oriented programming is still imperative, but it organises the imperative steps around the data they act on, which is what makes it a separate paradigm rather than a tidier version of the procedural one.
The syllabus asks for characteristics, not history. For each paradigm be ready to say what the programmer writes, what the language provides, what the paradigm is good at, where it is weak, and one situation in which you would choose it. That five-sentence shape answers almost every question on this section.
- Low-level: instructions close to machine code, direct control of registers and memory, machine dependent; fast and compact, but slow and difficult to write.
- Imperative (procedural): sequence, selection and iteration acting on variables, grouped into procedures and functions; the programmer states the steps, the how.
- Object-oriented: imperative code organised into classes and objects, with encapsulation, inheritance and polymorphism.
- Declarative: facts and rules, and a goal to satisfy; the system decides the order in which they are applied, the what.
- A paradigm is not a language: one language can support several paradigms, and the same problem can be written in any of them.
- A paradigm is not a program: it is a way of thinking about a solution, not a particular solution.
| Paradigm | What the programmer writes | Built around | Typical use |
|---|---|---|---|
| Low-level | Instructions that name registers, memory addresses and addressing modes | The processor and its instruction set | Device drivers, embedded controllers, code where speed, size or timing is critical |
| Imperative (procedural) | A sequence of statements using sequence, selection and iteration, grouped into procedures and functions | Variables, assignment and subroutines | General-purpose programs: calculations, file processing, simulations |
| Object-oriented | Classes that define attributes and methods, then objects created from them | Objects that hold both data and the methods that act on that data | Large programs with many related kinds of data: booking systems, games, user interfaces |
| Declarative | Facts and rules, and a goal to satisfy | The relationships described by the facts and rules | Rule-based reasoning, expert systems, database queries |
Low-level programming: mnemonics, registers and the machine
Low-level programming means writing instructions that correspond closely to the operations the processor itself performs. Each instruction is a single machine operation - load a value, add, store, compare, jump - written as a mnemonic with an operand, such as LDD 400 or ADD #5. An assembler translates each mnemonic into one machine code instruction, so the programmer works at the level of the machine rather than at the level of the problem.
The programmer has to do explicitly everything a high-level language does quietly: move values between memory and the accumulator (ACC), use the index register (IX) to work through a table, choose an addressing mode for every operand, and control the order of execution with jumps. Cambridge assembly questions assume a single general-purpose register, the accumulator, so almost every instruction is written around getting the right value into ACC, doing something to it, and storing it back.
That control is why low-level code still exists. It produces compact, fast code, it can reach hardware that a high-level language cannot name, and its timing is predictable, which matters in a device driver, an interrupt handler, a boot loader or the firmware of an embedded controller. It is also machine dependent: the instruction set belongs to one family of processors, so the program will not run on a different processor without being rewritten.
The cost is the programmer's time. Low-level code is long, hard to read, hard to debug and hard to maintain, because a page of it may do no more than add ten numbers. That is why high-level languages exist, and why you are asked to write and trace small low-level routines rather than to build whole systems in assembly.
- One mnemonic maps to one machine instruction: LDM #25 puts the number 25 into the accumulator.
- Registers and memory are named directly. Only ACC and IX are available, so values must be moved into them and stored back out.
- The addressing mode of every operand has to be chosen by the programmer, because the machine has no idea what the operand is meant to be.
- Machine dependent: the instruction set and the addresses belong to one processor family, so the code is not portable.
- Strengths: very fast, very compact, precise control of hardware and timing.
- Weaknesses: slow to write, hard to read and maintain, easy to get wrong, and no built-in structures such as arrays or records.
| Instruction group | Examples | What it does |
|---|---|---|
| Data movement | LDM #n, LDD address, LDI address, LDX address, LDR #n, STO address, MOV IX | Loads a value into ACC or IX, or stores the contents of ACC into memory |
| Arithmetic | ADD #n, ADD address, SUB #n, SUB address, INC IX, DEC IX | Adds to, subtracts from or adjusts the value held in a register |
| Comparison and branching | CMP #n, CMP address, JPE address, JPN address, JMP address | Compares ACC with a value, then jumps unconditionally or according to the result |
| Input and output | IN, OUT | Reads a character into ACC, or outputs the character whose ASCII value is in ACC |
| Ending the program | END | Returns control to the operating system |
Addressing modes: immediate, direct, indirect, indexed and relative
An addressing mode is the rule for interpreting the operand of an instruction. The operand field of a machine instruction is only a few bits wide, far too short to hold a full memory address or a large number, so the processor provides several modes that let a short operand reach any data it needs.
Immediate addressing puts the value itself in the operand: LDM #25 loads the number 25 into ACC, and the # shows that 25 is a value, not an address. It is the fastest mode, because no memory access is needed to fetch the operand, but the value is fixed when the program is written.
Direct addressing puts the address of the value in the operand: LDD 400 loads into ACC the contents of memory location 400. One extra memory access is needed and the instruction can only reach addresses that fit the operand field, but the contents of that address can change while the program runs, which is why direct addressing is used for variables.
Indirect addressing puts the address of an address in the operand: LDI 400 looks at location 400, finds an address stored there, and loads the contents of that second address into ACC. Two memory accesses are needed, but the operand does not have to hold the final address, so the program can reach anywhere in memory and can change its target by changing the pointer. This is how pointers, linked structures and passing parameters by reference are implemented.
Indexed addressing forms the address at run time by adding the contents of the index register to the address in the operand. If IX holds 6, LDX 400 loads the contents of address 406. The index register can be incremented on each pass of a loop, so one instruction walks through an array or a table, which is how arrays are handled in assembly language.
Relative addressing treats the operand as a displacement from the address of the current instruction rather than as an absolute address. A relative jump such as JMP +3 passes control to the instruction three places on from the present one, and the processor calculates the target by adding the displacement to the program counter. Because the operand is a distance rather than a fixed address, the code works wherever it is loaded in memory, which is what makes relocatable routines possible.
| Mode | What the operand is | Example | What reaches ACC | Why it is used |
|---|---|---|---|---|
| Immediate | The value itself | LDM #25 | 25 | Fast, no memory access for the operand; used for constants |
| Direct | The address of the value | LDD 400 | The contents of address 400 | A variable held at a known address |
| Indirect | The address of a location that holds the address | LDI 400, where location 400 holds 900 | The contents of address 900 | Pointers; the target can be changed while the program runs |
| Indexed | A base address, with the contents of IX added | LDX 400 with IX = 6 | The contents of address 406 | Arrays and tables; one instruction walks a list |
| Relative | A displacement from the current address | JMP +3 | Control passes three instructions forward | Jumps in code that may be moved in memory |
Imperative (procedural) programming and structured programming
Imperative programming is programming as a list of commands that change the state of the program. The program has variables, each holding a value, and each statement assigns a value, reads a value or decides what happens next. Procedural programming is imperative programming organised into procedures and functions: named blocks of code that can be called, can be given parameters and, in the case of a function, return a value.
Everything in the paradigm is built from three constructs. Sequence is one statement after another in the order written. Selection chooses between paths with IF ... THEN ... ELSE ... ENDIF or CASE ... OF ... ENDCASE. Iteration repeats a block with FOR ... NEXT, WHILE ... ENDWHILE or REPEAT ... UNTIL. A program of any size is these three constructs nested inside one another, plus subroutines.
Structured programming - called structural programming in the syllabus - is the discipline that keeps that nesting under control, and it is assumed knowledge from AS section 11.3. The program is designed top-down and broken into subroutines that each do one job; each subroutine has one entry point and one exit point; there is no GOTO, so control never jumps into the middle of a block; and the code is indented and named so that its structure can be read. Structured code is easier to test, because each subroutine can be tested alone, and easier to maintain, because a change is confined to the subroutine responsible for that job.
Procedures and functions are what make the paradigm scale. A procedure carries out an action and may change the values passed to it; a function returns a single value and can be used inside an expression. Parameters are passed by value, which copies the value so the original is untouched, or by reference, which passes the address so the subroutine can change the caller's variable. Variables declared inside a subroutine are local and cease to exist when it ends; variables declared outside are global and can be read and changed by any subroutine, which is convenient and dangerous in equal measure.
The weakness is the separation of code and data. The data of a program - a pupil, an order, an account - is spread across variables and arrays, and every procedure that touches it must know how it is stored. Change the layout of the data and every procedure that uses it has to change too, and nothing stops one procedure from writing nonsense into another's data by mistake. Object-oriented programming is the answer to exactly that problem.
- Sequence, selection and iteration are the only control constructs; every program is built from them.
- A procedure performs an action; a function returns a value.
- Parameters pass data in. By value copies the value; by reference passes its address, so the subroutine can change the caller's variable.
- Local variables exist only inside their subroutine; global variables are visible everywhere and are a common source of bugs.
- Structured programming: top-down design, one entry and one exit per subroutine, no GOTO, clear indentation and meaningful names.
- Strength: it matches the way a processor works, so it is natural to trace, test and translate.
- Weakness: data and the code that uses it are separate, so large programs become hard to maintain and hard to reuse.
| Construct | Keywords in pseudocode | When it is used |
|---|---|---|
| Sequence | Statements one after another | Always - it is the default order of execution |
| Selection | IF ... THEN ... ELSE ... ENDIF, CASE ... OF ... ENDCASE | When one of several paths must be chosen |
| Iteration, count-controlled | FOR ... TO ... NEXT | When the number of repetitions is known in advance |
| Iteration, condition-controlled | WHILE ... DO ... ENDWHILE, REPEAT ... UNTIL | When repetition depends on a condition |
| Subroutine | PROCEDURE ... ENDPROCEDURE, FUNCTION ... ENDFUNCTION | When a block of code does one job and is used more than once |
Object-oriented programming: classes, objects and encapsulation
Object-oriented programming (OOP) keeps the imperative constructs but changes what the program is built from. Instead of separate variables and procedures, a program is built from objects, and each object holds both its own data and the methods that act on that data. A class is the template describing what objects of that kind contain: its attributes (also called properties) and its methods. An object is an instance of a class, created while the program runs, with its own copy of the attributes.
A class is designed by looking at the problem for nouns and verbs. A booking system mentions customer, room and booking, so those become classes; a customer has a name and a telephone number, so those become attributes; a customer can be created, changed and checked in, so those become methods. Attributes are usually declared private and methods public, and the public methods that read and write a single attribute are its getters and setters. A getter returns the value of an attribute; a setter takes a new value, checks it and stores it.
Encapsulation is the combining of data and the methods that operate on it inside one class, together with the hiding of that data behind the methods. Because the attributes are private, no other part of the program can assign to them directly: it must call a setter, and the setter can reject a negative balance or a blank name. The object protects its own data, and it can be changed internally later without breaking the code that uses it, provided the public methods keep the same parameters.
Inheritance lets a new class be defined from an existing one. The subclass inherits the attributes and methods of the superclass, can add its own, and can override an inherited method with a version that behaves differently. Inheritance models an is-a relationship: a SavingsAccount is a BankAccount, so it can be used wherever a BankAccount is expected. Containment (aggregation) is different and is often confused with it: an object holds another object as one of its attributes, modelling a has-a relationship - a Car has an Engine, a Booking has a Room.
Polymorphism means that the same method call can behave differently for different objects. If BankAccount has a method CalculateInterest and SavingsAccount overrides it, calling CalculateInterest on a variable declared as BankAccount runs the version belonging to the object actually stored there. The calling code does not need to know which subclass it has, which is what lets a program hold a mixture of related objects in one array and process them in a single loop.
| Term | Meaning | Example |
|---|---|---|
| Class | The template that defines the attributes and methods of a type of object | CLASS BankAccount ... ENDCLASS |
| Object (instance) | One item created from a class, with its own values for the attributes | Account1 <- NEW BankAccount("A. Patel", 50.00) |
| Attribute (property) | A piece of data held by each object of the class | OwnerName : STRING, Balance : REAL |
| Method | A subroutine that defines what an object can do | Deposit(Amount), GetBalance() |
| Getter | A public method that returns the value of a private attribute without changing it | GetBalance() RETURNS REAL |
| Setter | A public method that validates a new value and stores it in a private attribute | SetOwnerName(Name : STRING) |
| Encapsulation | Combining data and methods in one class and hiding the data behind the methods | Private attributes reached only through getters and setters |
| Inheritance | A subclass takes the attributes and methods of a superclass and may add to or override them | CLASS SavingsAccount INHERITS BankAccount |
| Polymorphism | The same method call behaves according to the actual class of the object | CalculateInterest() runs the subclass version for a savings account |
| Containment (aggregation) | An object holds another object as one of its attributes, a has-a relationship | A Booking has a Room; a Car has an Engine |
Designing classes and writing object-oriented code
A question that asks you to design appropriate classes wants four things: the class names, their attributes with data types, their methods with parameters, and the relationships between them. Work from the scenario, not from a memorised list. Each noun the scenario stores data about is a class, each item of data it mentions is an attribute, each action it describes is a method, 'is a' between two nouns means inheritance and 'has a' means containment.
The class below shows the pattern. The attributes are private, the constructor and the methods are public, and the getter and setter are the only route to the balance. Deposit validates its parameter, and that check exists only because the balance cannot be changed from outside the class - encapsulation doing real work rather than being a definition to recite.
Inheritance and containment are chosen from the scenario. If the new class is a special case of an existing one that shares nearly all its attributes and methods, inherit: a SavingsAccount needs everything a BankAccount has, plus an interest rate. If the new class merely uses another object, contain it: a Booking is not a Room, it holds one. Inheriting when you should contain produces a class that drags round attributes it does not need and breaks when the superclass changes.
Write the code the way the marks are awarded: declare the class, declare its attributes with types, declare its methods with parameters, and use the object only through its public methods. A design with no methods, or with every attribute public, loses the marks for encapsulation and for the behaviour the scenario described.
| Line of pseudocode | What it shows |
|---|---|
| CLASS BankAccount | Declares the class: the template for every account object |
| PRIVATE OwnerName : STRING | A private attribute, hidden from the rest of the program |
| PRIVATE Balance : REAL | A second attribute; each object gets its own copy of both |
| PUBLIC PROCEDURE New(Owner : STRING, Opening : REAL) | A constructor: creates an instance and sets its initial state |
| Balance <- Opening | Assignment to the object's own attribute |
| PUBLIC FUNCTION GetBalance() RETURNS REAL | A getter: returns the attribute without allowing it to be changed |
| PUBLIC PROCEDURE Deposit(Amount : REAL) | A method that changes the state of the object |
| IF Amount > 0 THEN Balance <- Balance + Amount ENDIF | Validation inside the class, which encapsulation makes possible |
| ENDCLASS | Ends the class definition; no object exists yet |
| Account1 <- NEW BankAccount("A. Patel", 50.00) | Creates one instance of the class |
| OUTPUT Account1.GetBalance() | Calls a method on that object using dot notation |
| CLASS SavingsAccount INHERITS BankAccount | A subclass that inherits the attributes and methods of the superclass |
| PUBLIC PROCEDURE AddInterest() | A method that exists only in the subclass, after the interest rate is added as an attribute |
| PUBLIC PROCEDURE Deposit(Amount : REAL) | The subclass declares Deposit again, overriding the inherited version |
Declarative programming: facts, rules and goals
Declarative programming describes what is true rather than what to do. A declarative program is a set of facts and rules, and it is used by putting a goal to it - a question - which the system tries to satisfy by applying the facts and rules. There is no assignment, no loop and no sequence of statements to follow: the programmer supplies the knowledge and the system works out the order in which to use it.
A fact is a statement that is unconditionally true, written as a relation with its arguments. In Prolog, parent(tom, bob). states that tom is a parent of bob, and female(ann). states that ann is female. Lower-case words are atoms - fixed names - and a name beginning with a capital letter is a variable, which can stand for anything.
A rule states something that is true provided other things are true. It has a head and a body, and the body is a list of conditions that must all hold. In pseudocode the rule is written grandparent(X, Z) IF parent(X, Y) AND parent(Y, Z); in Prolog the same rule is written grandparent(X, Z) :- parent(X, Y), parent(Y, Z), where :- is read as 'if' and the comma as 'and'. This says that X is a grandparent of Z if there is some Y such that X is a parent of Y and Y is a parent of Z. The variable Y is the link between the two conditions, so one rule answers the question for every family in the facts.
A goal is the question put to the program, such as grandparent(tom, Who). The system matches the goal against the head of a fact or rule, binding variables as it goes (unification); if a condition in the body cannot be satisfied it backtracks to the last choice it made and tries the next possibility, and it continues until it has found every solution or exhausted the possibilities. That search is the 'how', and the programmer never writes it.
The paradigm suits problems that are naturally a set of relationships or rules: family relationships, a diagnostic or advisory expert system that reasons from symptoms to a conclusion, a timetable or route that must satisfy constraints, and a database query. It is poor at problems that are a sequence of actions with input and output, such as a game loop or a user interface, and a badly written set of rules can make the search very slow.
- Fact: unconditionally true, for example parent(tom, bob).
- Rule: true if its body is true, for example grandparent(X, Z) IF parent(X, Y) AND parent(Y, Z), written in Prolog as grandparent(X, Z) :- parent(X, Y), parent(Y, Z).
- Goal: the question asked of the program, for example grandparent(tom, Who).
- Variable: a name beginning with a capital letter, bound to different values during matching.
- Backtracking: when a condition fails, the system returns to the last choice and tries the next possibility.
- Recursion replaces iteration, as in ancestor(X, Y) IF parent(X, Y) together with ancestor(X, Z) IF parent(X, Y) AND ancestor(Y, Z).
- Strength: concise, easy to extend with new facts, and the knowledge is kept separate from the search.
- Weakness: no natural input, output or sequence; the search can be slow; the programmer has less control over how the answer is found.
| Part of the program | What it is | Example |
|---|---|---|
| Fact | A statement that is always true | parent(tom, bob). |
| Rule head | The conclusion the rule proves | grandparent(X, Z) |
| Rule body | The conditions that must all be true | parent(X, Y) AND parent(Y, Z) |
| Goal | The question the user asks | grandparent(tom, Who) |
| Variable | A placeholder bound during matching | X, Y, Z, Who |
File processing: modes, records and the three file organisations
A program that keeps its data only in variables loses it when it ends, so data that must survive is written to a file on secondary storage. Every file operation follows the same pattern: open the file in the right mode, read or write records, then close it, because closing flushes any buffered data to the disk and releases the file.
The mode is chosen from what the program is about to do. FOR READ opens an existing file so that data can be read and nothing written. FOR WRITE creates a new file, or empties an existing one, and is used when the whole file is being produced from scratch - it destroys whatever was there. FOR APPEND opens an existing file and positions the write after the last record, which is how records are added without losing the others. FOR RANDOM opens a file of fixed-length records for direct access, so that a record can be read or replaced by its position.
A record is one complete item in the file - one pupil, one order, one booking - held as a set of fields of different types. In a text file a record is normally one line, so reading a record means reading a line with READFILE and splitting it into its fields, and writing a record means joining the fields into a line and using WRITEFILE. In a random file the record is written whole with PUTRECORD and read whole with GETRECORD into a variable of the record type.
A serial file holds records in the order they were added, with no ordering by any key; to find one record you must read from the beginning, so it is used where every record is processed anyway, such as a transaction log or a backup. A sequential file holds records in order of a key field, such as a pupil number or an account number, and is read in that order; a record is found by reading through until the key matches, and adding or deleting one in the middle means rewriting the file, which is why sequential files are updated in a batch by merging an old master file with a transaction file to produce a new master file. A random file holds fixed-length records at known positions, so SEEK moves the file pointer straight to a record and GETRECORD or PUTRECORD reads or writes it without touching the records before it, which makes random files fast for looking up and updating individual records.
Choose by how the data will be used. If every record is processed in one run and the order does not matter, serial is enough. If the data must be processed in key order, sequential is right - payroll, billing and end-of-term reports are all like this, and sequential files suit magnetic tape because tape can only be read in order. If individual records must be found and changed quickly, random is right, but all the records must be the same length and there must be a way to calculate the address from the key.
- Open modes: READ for an existing file, WRITE to create or empty a file, APPEND to add at the end without losing what is there, RANDOM for fixed-length records and direct access.
- Read a text file: OPENFILE ... FOR READ, WHILE NOT EOF(...) DO, READFILE one line, process it, ENDWHILE, CLOSEFILE.
- Add a record without destroying the others: open FOR APPEND and write one record; FOR WRITE would empty the file first.
- Update a random file: open FOR RANDOM, SEEK the position, GETRECORD, change the fields, SEEK the same position again, PUTRECORD, close.
- Serial: order added, sequential access only, every record processed - a transaction log or a backup.
- Sequential: ordered by a key field, read in key order, updated in a batch by merging a master file with a transaction file.
- Random: fixed-length records at calculated positions, reached directly with SEEK - fast lookup and update of single records.
- Close every file: CLOSEFILE flushes buffered records to disk, and a program that forgets it can lose the last records it wrote.
| Statement | What it does |
|---|---|
| OPENFILE "pupils.txt" FOR READ | Opens an existing text file so that records can be read |
| OPENFILE "pupils.txt" FOR WRITE | Creates a new text file, or empties an existing one |
| OPENFILE "pupils.txt" FOR APPEND | Opens a text file so that new records go after the last one |
| OPENFILE "pupils.dat" FOR RANDOM | Opens a file of fixed-length records for direct access |
| READFILE "pupils.txt", LineOfText | Reads the next line of a text file into a variable |
| WRITEFILE "pupils.txt", LineOfText | Writes one line to a text file |
| SEEK "pupils.dat", 10 | Moves the file pointer to record position 10 |
| GETRECORD "pupils.dat", Pupil | Reads the record at the pointer into a record variable |
| PUTRECORD "pupils.dat", Pupil | Writes the record variable at the pointer, replacing what was there |
| EOF("pupils.txt") | TRUE once the whole file has been read |
| CLOSEFILE "pupils.txt" | Flushes the file to disk and releases it |
Exceptions and exception handling
An exception is an error or unexpected event that happens while a program is running and that the normal flow of the program cannot deal with: a file that does not exist, a division by zero, text that cannot be converted into a number, an index outside the bounds of an array. It is detected by the system rather than by the program's own IF statements, and if nothing deals with it the program terminates at that point, losing whatever was in memory and showing the user an error message that means nothing to them.
Exception handling is the facility that lets a program say what to do when an exception occurs. The code that might fail goes in a TRY block and the code that deals with the failure goes in an EXCEPT block. If every statement in the TRY block runs without an exception, the EXCEPT block is skipped; if any statement in it raises an exception, the rest of the TRY block is abandoned and control passes straight to the EXCEPT block, after which the program continues with the statement after ENDTRY.
The importance of handling is in that last sentence. Without it, one bad record or one missing file ends the whole run. With it, the program can report the problem, use a default value, ask for another file name, write the record to a reject file, or close the files it has open and stop cleanly - and it can do so in one place instead of after every statement. Handling also keeps the main logic readable, because checks for things that should never happen are separated from the code that does the work.
Use exception handling where the failure is outside the program's control and cannot be ruled out in advance: opening a file that may not exist or may be locked, reading past the end of a file, converting text typed by a user into a number, dividing by a value that might be zero, and any input from a network or a device. Do not use it in place of validation where the error is predictable - a mark out of 100 should be checked with an IF statement, because that is ordinary data handling and the check belongs in the logic. The rule of thumb is that validation deals with input that is wrong, and exception handling deals with a situation the program could not prevent.
The block below is the pattern to write in an exam. The TRY block contains the whole file operation, including the close, so that a failure at any point is caught, and the EXCEPT block reports the problem in words the user can act on. In a real language the block looks the same with different keywords: Java uses try and catch, and Python uses try and except.
| Line of pseudocode | What it does |
|---|---|
| TRY | Starts the block of code that might raise an exception |
| OPENFILE "pupils.txt" FOR READ | The operation most likely to fail, because the file may not exist |
| WHILE NOT EOF("pupils.txt") DO ... ENDWHILE | The normal processing, kept inside the protected block |
| READFILE "pupils.txt", LineOfText | Reads each record; this too can fail, for example on a damaged file |
| CLOSEFILE "pupils.txt" | Closing inside the block, so the file is released on the normal path |
| EXCEPT | Runs only if a statement in the TRY block raised an exception |
| OUTPUT "The file could not be opened or read." | The recovery: tell the user in words they can act on |
| ENDTRY | Ends the block; the program continues after this line |
Key terms you must be able to define
- Programming paradigm
- A style or approach to writing programs, defined by the features a language provides and the way a problem is broken down. It is not the same thing as a language, because one language can support several paradigms.
- Low-level programming
- Programming with instructions that correspond closely to the processor's own machine operations, naming registers, memory addresses and addressing modes. An assembler translates each mnemonic into one machine code instruction, and the code is machine dependent.
- Addressing mode
- The rule that decides how the operand of an instruction is interpreted: whether it is the value itself, the address of the value, or something from which the address is calculated. The five in this syllabus are immediate, direct, indirect, indexed and relative.
- Imperative (procedural) programming
- A paradigm in which the programmer writes the steps that change the state of the program, using sequence, selection and iteration on variables, with procedures and functions to group the steps into named blocks.
- Structured programming
- The discipline of building a program from sequence, selection and iteration only, designed top-down into subroutines that each have one entry and one exit, with no GOTO, so that the structure of the code is visible and each part can be tested on its own.
- Class
- The template that defines the attributes and the methods that objects of that type will have. It describes the objects; it is not itself an object, and no storage is created until an instance is made.
- Object (instance)
- One item created from a class while the program runs, with its own copy of the attributes and access to the methods defined by the class.
- Encapsulation
- The combining of data and the methods that operate on it inside one class, together with the hiding of the data behind those methods, so that attributes can only be read and changed through public getters and setters that can validate the change.
- Inheritance
- The mechanism by which a subclass takes the attributes and methods of a superclass, may add its own, and may override an inherited method. It models an is-a relationship, such as a savings account being a kind of bank account, and it promotes reuse of tested code.
- Polymorphism
- The property that the same method call behaves differently according to the actual class of the object it is made on, so code can process a mixture of related objects in one loop without knowing which subclass each one belongs to.
- Containment (aggregation)
- A relationship in which an object holds another object as one of its attributes, modelling a has-a relationship, such as a booking holding a room. It is not inheritance, which models is-a.
- Declarative programming
- A paradigm in which the programmer writes facts and rules that describe what is true, and puts a goal to the system, which works out the order in which to apply the facts and rules. There is no assignment and no explicit loop or sequence.
- Serial, sequential and random files
- Three ways of organising a file. Serial: records in the order they were added, read sequentially from the start. Sequential: records in order of a key field, read in that order and updated in a batch. Random: fixed-length records at positions calculated from the key, reached directly with SEEK and GETRECORD or PUTRECORD.
- Exception handling
- The facility that lets a program respond to a run-time error instead of terminating. The code that may fail is placed in a TRY block and the recovery in an EXCEPT block, so the program can report the problem, recover and carry on.
Applied to real systems
9618 sets its questions inside systems you already use - a phone, a school network, a supermarket till, a website, a bank. These show how to apply the theory to a context you have not seen before, then you write your own.
A microcontroller inside a washing machine or a car's engine management unit has to read a sensor at a fixed, memory-mapped address and respond within a guaranteed number of clock cycles. That code is written in assembly: LDD reads the sensor's address directly, LDX with the index register steps through a table of settings, and immediate loads put constants into the accumulator. A high-level language would hide the exact addresses and the exact timing, which is precisely what this code cannot allow, and the same program will not run on a different processor family without being rewritten. It is the clearest case of a situation where low-level control matters more than the programmer's time.
A banking system holds each account as an object of class BankAccount, with OwnerName and Balance private and Deposit, Withdraw and GetBalance public, so the balance can only change through code that checks the amount. SavingsAccount inherits from it, adds an interest rate and overrides CalculateInterest, while CurrentAccount inherits too, adds an overdraft limit and overrides Withdraw so that a withdrawal past the limit is refused. One array of BankAccount can hold both kinds, and a single loop calling CalculateInterest runs the right version for each object, which is polymorphism. Containment appears alongside inheritance: a Customer has an Address, and a Branch has a set of Accounts.
A query such as SELECT Surname FROM Pupils WHERE YearGroup = 12 states what result is wanted and says nothing about how to get it; the database management system's query optimiser decides whether to scan the table or to use an index. This is the same division of labour as a Prolog goal: the programmer supplies the facts, in the form of tables, and the condition, in the form of the query, and the system chooses the order of the search. It is the everyday example of declarative programming, and a good contrast with a procedural loop that reads every pupil record and tests the year group itself.
A payroll system keeps a master file sorted by employee number and, during the month, writes each change to a transaction file as it happens. At the end of the month a batch program reads both files in employee-number order, merges them, applies the changes and writes a new master file, passing through each sequential file once from beginning to end. This works because both files are in the same key order, so the program can process them side by side without searching, and the old master file is kept until the new one has been checked. It also shows the weakness of the organisation: one change cannot be made on its own, because the whole file has to be rewritten.
A program is asked to read yesterday's sales from sales.txt. If the file has been moved, renamed or never created, an unhandled exception stops the program dead and the user sees a system error message with no idea what to do about it. Wrapped in TRY ... EXCEPT ... ENDTRY, the same program reports that sales.txt could not be opened, offers to let the user choose another file or to carry on with an empty day, and closes anything it had already opened. The error still happened, but the program survived it and the user was told what to do next.
Flashcards
Click a card to flip it. Mark honestly — the tracker uses this.
Exam-style questions
8 questions, 28 marks. Write your answer, then reveal the mark scheme and tick each point you actually made. Answering honestly here is what makes the rest of the site useful.
Mark scheme — tick each point you actually made
Mark scheme — tick each point you actually made
Mark scheme — tick each point you actually made
Mark scheme — tick each point you actually made
Mark scheme — tick each point you actually made
Mark scheme — tick each point you actually made
Mark scheme — tick each point you actually made
Mark scheme — tick each point you actually made
Exam technique for this topic
- For a question on the characteristics of a paradigm, write the same five sentences each time: what the programmer writes, what the language provides, what it is good at, what it is poor at, and one situation where you would choose it. Marks are for the distinctive features, not for a definition of the word.
- For addressing modes, state what the operand is every time: the value (immediate), the address of the value (direct), the address of the address (indirect), an address plus the index register (indexed), or a displacement from the current address (relative). A trace showing ACC, IX and the contents of the address before and after is the safest way to earn every mark.
- In an OOP answer, use the terminology inside a sentence about the class you have designed: 'Balance is private, so it can only be changed through Deposit, which is encapsulation'. A list of definitions with no reference to the scenario rarely gets past half marks, and inheritance has to be justified by an is-a relationship.
- In a declarative answer, label the parts explicitly: the head is the conclusion, the body is the conditions, and the variables are the links between them. A statement with no body is a fact, and a set of facts with no rules cannot answer a question that needs reasoning.
- In a file question, use the exact command names in capitals - OPENFILE, READFILE, WRITEFILE, CLOSEFILE, SEEK, GETRECORD, PUTRECORD, EOF - and check the order: open in the right mode, loop while not EOF, read one record, process it, close. Put the operations inside a TRY block whenever the question mentions a file that might not exist.