Data types and programming constructs

The A level data types, references and user-defined types, constants, and definite and indefinite iteration.

A1.1Programming techniques and object-oriented programmingA level20 min

Do this lesson in the simulator

At 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)

Run this in the simulator

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__)

Run this in the simulator

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")

Run this in the simulator

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_CM says 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 ... UNTIL or do ... 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)

Run this in the simulator

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

  1. Add a fourth field, taken, holding the time of the reading from clock(). What type is it?
  2. Rewrite the survey loop as a condition-at-the-end loop that stops when the robot has turned a full circle.
  3. Try r.cm = 0 on one of your records. What happens, and why might that be useful for sensor data?