The Walrus Operator

500px-Noaa-walrus22.jpg

Let's suppose that we need to process a set of instructions to a robot. The instructions are in a file, one per line, and they are of two kinds:

Our goal is to read a file containing such instructions and determine where the robot is after executing them. The robot starts at the origin (0,0), facing east (i.e. along to positive x-axis).

Let's begin by writing a function that outputs such a list of instructions at random.

from random import seed, randint

seed()


def random_instructions(count=8, unit_max=8):
    """
    Write a number of robot instructions given by count.  The turns are in
    multiples of 10 degrees.
    """
    for i in range(count):
        if 0 == randint(0, 1):
            units = randint(1, unit_max)
            print(f"MOVE {units:4d}")
        else:
            degrees = 10 * randint(-18, 18)
            print(f"TURN {degrees:4d}")

Let's try it out.

random_instructions()
TURN  -60
MOVE    2
MOVE    7
MOVE    1
MOVE    3
MOVE    1
MOVE    6
TURN  110

Now let's write some instructions to a file.

from contextlib import redirect_stdout

INSTRUCTION_FILE = "/tmp/robot-instructions.txt"

with open(INSTRUCTION_FILE, "w") as rif:
    with redirect_stdout(rif):
        random_instructions(16)

Now we turn to reading the instructions.

def follow_instructions(filename):
    with open(filename) as insts:
        for line in insts.readlines():
            print(line, end="")


follow_instructions(INSTRUCTION_FILE)
TURN -140
TURN  -90
TURN   80
TURN   20
MOVE    4
MOVE    3
TURN   20
MOVE    3
TURN  180
TURN   80
TURN  140
MOVE    7
TURN -120
MOVE    4
MOVE    5
TURN  150

Note how each line is read including its final newline. The print statement then prints the line without adding another newline.

def follow_instructions(filename):
    with open(filename) as insts:
        for line in insts.readlines():
            print(line, end="")


follow_instructions(INSTRUCTION_FILE)
TURN -140
TURN  -90
TURN   80
TURN   20
MOVE    4
MOVE    3
TURN   20
MOVE    3
TURN  180
TURN   80
TURN  140
MOVE    7
TURN -120
MOVE    4
MOVE    5
TURN  150

There are several ways we could now continue but we chose to use regular expressions so that we can demonstrate the walrus operator.

from re import compile, match

RE_MOVE = compile(r"MOVE +(\d+)")
RE_TURN = compile(r"TURN +(-?\d+)")


def follow_instructions(filename):
    with open(filename) as insts:
        for line in insts.readlines():
            if m := match(RE_MOVE, line):
                print("moving", m.group(1), "units")
            elif m := match(RE_TURN, line):
                print("turning", m.group(1), "degrees")
            else:
                print("Failed to parse:", line)


follow_instructions(INSTRUCTION_FILE)
turning -140 degrees
turning -90 degrees
turning 80 degrees
turning 20 degrees
moving 4 units
moving 3 units
turning 20 degrees
moving 3 units
turning 180 degrees
turning 80 degrees
turning 140 degrees
moving 7 units
turning -120 degrees
moving 4 units
moving 5 units
turning 150 degrees

Notice how match(RE_MOVE, line) tries to match the MOVE regular expression against line. If it fails it returns None and the if statement fails. However if it succeeds it returns a match object which makes the if statement succeed. The walrus operator := captures the result of the match in the variable m. This can then be used in the body of the if statement.

Now let's fill in the actual actions. Note that the trigonometric operations in Python's math library take their arguments in radians so we, write functions that take their arguments in degrees.

from math import pi, cos, sin


def d2r(d):
    return pi * d / 180


def cosd(d):
    return cos(d2r(d))


def sind(d):
    return sin(d2r(d))


def follow_instructions(filename, location=None, orientation=0):
    if not location:
        location = [0, 0]
    with open(filename) as insts:
        for line in insts.readlines():
            if m := match(RE_MOVE, line):
                units = int(m.group(1))
                location[0] += units * cosd(orientation)
                location[1] += units * sind(orientation)
                print(f"MOVE {units:4d}")
            elif m := match(RE_TURN, line):
                degrees = int(m.group(1))
                orientation += degrees
                print(f"TURN {degrees:5d}")
            else:
                print("Failed to parse:", line)
            print(f"{orientation:4d} ({location[0]:4.2f}, {location[1]:4.2f})")
    print(location)


follow_instructions(INSTRUCTION_FILE)
TURN  -140
-140 (0.00, 0.00)
TURN   -90
-230 (0.00, 0.00)
TURN    80
-150 (0.00, 0.00)
TURN    20
-130 (0.00, 0.00)
MOVE    4
-130 (-2.57, -3.06)
MOVE    3
-130 (-4.50, -5.36)
TURN    20
-110 (-4.50, -5.36)
MOVE    3
-110 (-5.53, -8.18)
TURN   180
  70 (-5.53, -8.18)
TURN    80
 150 (-5.53, -8.18)
TURN   140
 290 (-5.53, -8.18)
MOVE    7
 290 (-3.13, -14.76)
TURN  -120
 170 (-3.13, -14.76)
MOVE    4
 170 (-7.07, -14.06)
MOVE    5
 170 (-11.99, -13.20)
TURN   150
 320 (-11.99, -13.20)
[-11.994702471612976, -13.196403710689557]

If we want to check this more carefully we could write a set of tests but we can gain some confidence by making some simple sets of instructions.

def random_instructions(count=8, unit_max=8):
    """
    Write a number of robot instructions given by count.  The turns are in
    multiples of 90 degrees.
    """
    for i in range(count):
        if 0 == randint(0, 1):
            units = randint(1, unit_max)
            print(f"MOVE {units:4d}")
        else:
            degrees = 90 * randint(-4, 4)
            print(f"TURN {degrees:4d}")


with open(INSTRUCTION_FILE, "w") as rif:
    with redirect_stdout(rif):
        random_instructions(4)

follow_instructions(INSTRUCTION_FILE)
TURN  -180
-180 (0.00, 0.00)
TURN   270
  90 (0.00, 0.00)
TURN   -90
   0 (0.00, 0.00)
MOVE    7
   0 (7.00, 0.00)
[7.0, 0.0]

Author: Breanndán Ó Nualláin <o@uva.nl>

Date: 2026-05-07 Thu 09:09