Data types and programming constructs
The A level data types, references and user-defined types, constants, and definite and indefinite iteration.
Do this lesson in the simulatorAt GCSE you met five data types (F1.8): integer, real, Boolean, character and string, and the three constructs sequence, selection and iteration (F2). A level keeps all of that and asks for more precision. There are more types to know, including ones you build yourself, and the exam expects the exact names for kinds of loop and for the difference between a variable and a constant.
The types you need to know
A data type says what values a piece of data can hold and what operations make sense on it. You can add two integers, but not two Booleans; you can take the length of a string, but not of a real.
| Type | What it holds | In Python | On the robot |
|---|---|---|---|
| Integer | Whole numbers | int |
Steps taken: 7 |
| Real (float) | Numbers with a fractional part | float |
A distance reading: 31.0 |
| Boolean | True or False |
bool |
Is the way clear? |
| Character | One symbol | a str of length 1 |
A command letter: "F" |
| String | A sequence of characters | str |
A command: "F20" |
| Date/time | A point in time | datetime |
When a reading was taken |
| Pointer/reference | Where a value is stored, not the value | every Python variable | Two names for one list of readings |
| Record | Named fields of different types together | a class, namedtuple or dict |
heading, distance and clear, as one reading |
| Array | Items of the same type, by index | list |
Eight readings round the robot |
Python has no separate character type: a character is a string one character long. It also has no pointer type you can see, because every variable already holds a reference, which is the next idea.
References: two names, one value
A pointer or reference holds the location of a value rather than the value itself. In Python, assignment copies the reference, not the value, so two variables can refer to one list:
readings = [31.0, 51.0]
same = readings # copies the reference: one list, two names
same.append(15.0)
print(readings)
print(same is readings)
copy = list(readings) # a new list with the same items
copy.append(99.0)
print(readings)
print(copy)
The first print shows 15.0 in readings, even though it was appended through same. is checks whether two references point at the same object. list(readings) builds a new list, so changing copy leaves the original alone. This matters again when a list is passed to a subroutine in lesson A1.4.
User-defined types
A user-defined data type is one the programmer builds from the language's built-in types. At GCSE a record was a dictionary; the problem is that nothing stops a typing mistake like r["distnce"] making a new field. A defined record type fixes the fields once:
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
from collections import namedtuple
Reading = namedtuple("Reading", ["heading", "cm", "clear"])
r = Reading(0, distance(), distance() > 40)
print(r)
print("the cm field holds", r.cm, "of type", type(r.cm).__name__)
print("the clear field holds", r.clear, "of type", type(r.clear).__name__)
Reading is now a type, like int. Each field is reached with a dot, r.cm, and a misspelt field name is an error rather than a silent new field. A namedtuple cannot be changed once made; lesson A1.7 builds types that can, with classes.
Declaration, assignment and constants
In many languages a variable must be declared before it is used, giving its name and type, as int steps = 0; does in C. Python declares a variable the first time it is assigned, and works the type out from the value. A type hint such as steps: int = 0 records the intended type for a reader and for checking tools, but Python does not enforce it.
A constant is a named value that does not change while the program runs. Python has no way to lock a name, so the convention is capital letters:
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
SAFE_CM = 25 # a named constant
STEP_CM = 5
steps = 0 # a variable
while distance() > SAFE_CM:
forward(50, distance=STEP_CM)
steps = steps + 1
print(steps, "steps of", STEP_CM, "cm")
The advantages of a named constant over typing 25 wherever it is needed:
- One change updates every use, so the safe distance cannot be 25 in one place and 30 in another.
- The name explains the value:
SAFE_CMsays what 25 is for. - It cannot be changed by mistake in a language that enforces constants, and the capitals warn you off in Python.
Iteration: definite and indefinite
Definite iteration repeats a number of times known before the loop starts: a for loop over range(4). Indefinite iteration repeats until a condition changes, and the number of times is not known in advance. It comes in two forms:
- Condition at the start (a pre-condition loop,
WHILE): the condition is checked first, so the body may run zero times. The creeping loop above does nothing if the robot starts close to the wall. - Condition at the end (a post-condition loop,
REPEAT ... UNTILordo ... until): the body runs first, so it always runs at least once.
Python has only the first kind. A condition-at-the-end loop is written with while True and a break at the bottom:
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
steps = 0
while True:
forward(50, distance=5) # always runs at least once
steps = steps + 1
if distance() <= 25: # the UNTIL condition
break
print(steps)
In AQA's pseudo-code the two loops are:
WHILE distance > 25
steps ← steps + 1
ENDWHILE
REPEAT
steps ← steps + 1
UNTIL distance ≤ 25
Nested selection and iteration means one construct inside another, like an if inside a loop. And meaningful identifier names such as steps and SAFE_CM, rather than s and x, make code easier to read, check and maintain.
Task: readings as a record type
Define a record type Reading with namedtuple and three fields: heading (an integer, 0, 90, 180 or 270), cm (the real number distance() returns) and clear (a Boolean, True when cm is more than the constant CLEAR_CM). Declare CLEAR_CM = 40 as a constant and use the name, not 40, in the comparison.
The robot starts facing heading 0. Take a reading, store it in an array, turn right 90 degrees, and repeat until there are four readings and the robot faces its starting direction. Then print each record with print(r), which shows Reading(heading=90, cm=51.0, clear=True), and finally print clear directions: <n>, the number of records whose clear field is True.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
from collections import namedtuple
Reading = namedtuple("Reading", ["heading", "cm", "clear"])
survey = []
# take four readings, one every 90 degrees
print("clear directions:", 0)
Challenges
- Add a fourth field,
taken, holding the time of the reading fromclock(). What type is it? - Rewrite the survey loop as a condition-at-the-end loop that stops when the robot has turned a full circle.
- Try
r.cm = 0on one of your records. What happens, and why might that be useful for sensor data?