Problem Solving — CS & DS

Lecture 14 — Functions and Reuse

Learning objectives

Case-based lecture: you attempt, we discuss, then we reveal.

Today

The plan

Key definitions

Key definitions

Explain each in one breath; the cases will do the heavy lifting.
Worked example

The validator trio: three contracts, one driver

cs-053

Write three independent checks - email-ish, phone-ish, student-ID - and a driver that prints a verdict table for sample values.

def looks_like_email(s):
    return s.count("@") == 1 and "." in s.split("@")[1]

def looks_like_phone(s):
    digits = s.replace(" ", "")
    return digits.isdigit() and len(digits) in (10, 11)

def looks_like_id(s):
    return len(s) >= 2 and s[0].isalpha() and s[1:].isdigit()

for v in ["ada@uni.edu", "07123 456789", "S2023/4471"]:
    print(v, looks_like_email(v), looks_like_phone(v), looks_like_id(v))

Expected: three verdict columns for the three sample values

Cost: Each check O(len(s)); the trio demonstrates decomposition, not performance.

Worked example

Refactor the monolith: same output, new shape

cs-054

One script parses 'name,course,score' lines, drops blanks, averages per student, prints a report. Refactor it into functions without changing behaviour.

def parse_line(line):
    parts = [p.strip() for p in line.split(",")]
    if len(parts) != 3 or not parts[2]:
        return None
    return parts[0], parts[1], float(parts[2])

def averages(rows):
    acc = {}
    for name, _course, score in rows:
        acc.setdefault(name, []).append(score)
    return {k: sum(v) / len(v) for k, v in acc.items()}

lines = ["ada,CS1,80", "bo,DS1,", "ada,CS1,95"]
rows = [r for r in (parse_line(l) for l in lines) if r]
avg = averages(rows)
print(round(avg["ada"], 1), len(rows))

Expected: averages: ada 87.5 (bo flagged, not silently dropped)

Cost: Unchanged O(n) - refactoring changes shape, not cost.

Worked example

The parameter jungle: the mutable default

cs-055

def add_tag(tag, tags=[]): called three times accumulates instead of starting fresh. Explain, then fix.

def add_tag(tag, tags=None):
    if tags is None:
        tags = []
    tags.append(tag)
    return tags

a = add_tag("x")
b = add_tag("y")
print(a, b, a is b)

Expected: buggy: calls accumulate in one shared list; fixed: each call gets its own list

Cost: No performance argument either way - this is a semantics lesson.

Worked example

Pipelines as functions: clean, tokenise, count

cs-056

Turn ' the CAT, the dog! ' into a word count. Build it as three small functions composed in a pipeline.

def clean(s):
    return " ".join(s.split())

def tokenise(s):
    return s.split(" ")

def count(words):
    d = {}
    for w in words:
        d[w] = d.get(w, 0) + 1
    return d

text = "  the CAT, the dog!  "
words = tokenise(clean(text))
tally = count(words)
print(words)
print(tally["the"] == 2, tally.get("CAT,"))

Expected: the:2, CAT,:1, dog!:1 (punctuation shows why cleaning rules matter)

Cost: O(n) total; the pipeline adds no cost, only clarity.

Case attempt · lecture 14
Case cs-053 · Beginner

The Validator Trio

Three validation checks (email-ish, phone-ish, id format) become three functions plus a driver. Naming, single purpose, return vs print.

⏱ 5 minutes — attempt now

Pseudocode, flowchart or Python. State one assumption. Compare with your neighbour at minute 4.

Discussion

cs-053 — discuss

Instructor: the reveal, the reasoning section and common wrong turns are in the instructor distribution for cs-053 — not embedded in this deck.

Case attempt · lecture 14
Case cs-054 · Foundational

Refactor the Monolith

One 40-line script computes grade statistics. Decompose it into functions without changing behaviour, then defend the boundaries chosen.

⏱ 5 minutes — attempt now

Pseudocode, flowchart or Python. State one assumption. Compare with your neighbour at minute 4.

Discussion

cs-054 — discuss

Instructor: the reveal, the reasoning section and common wrong turns are in the instructor distribution for cs-054 — not embedded in this deck.

Case attempt · lecture 14
Case cs-138 · Foundational

The Downtime Ledger

The IT helpdesk logged last week's outages, one line per incident:

⏱ 5 minutes — attempt now

Pseudocode, flowchart or Python. State one assumption. Compare with your neighbour at minute 4.

Discussion

cs-138 — discuss

Instructor: the reveal, the reasoning section and common wrong turns are in the instructor distribution for cs-138 — not embedded in this deck.

Case attempt · lecture 14
Case cs-055 · Intermediate

The Parameter Jungle

A function with a mutable default argument accumulates state across calls. Diagnose, fix, and generalise the lesson to API design.

⏱ 5 minutes — attempt now

Pseudocode, flowchart or Python. State one assumption. Compare with your neighbour at minute 4.

Discussion

cs-055 — discuss

Instructor: the reveal, the reasoning section and common wrong turns are in the instructor distribution for cs-055 — not embedded in this deck.

Case attempt · lecture 14
Case cs-056 · Expert

Pipelines as Functions

Build a text pipeline (clean, tokenise, count, report) where each stage is a function returning the next stage's input. Composition and contracts.

⏱ 5 minutes — attempt now

Pseudocode, flowchart or Python. State one assumption. Compare with your neighbour at minute 4.

Discussion

cs-056 — discuss

Instructor: the reveal, the reasoning section and common wrong turns are in the instructor distribution for cs-056 — not embedded in this deck.

Check yourself

Common misconceptions

Summary

Summary & exit question

Exit question (choose one, 60 seconds)

  1. What makes a function name good?
  2. Why are mutable defaults dangerous?
  3. What does 'refactor without behaviour change' demand?
Expected answers (instructor): it states the subproblem's contract: verb + object, no surprises the default object is created once and shared across calls the same tests pass before and after, unchanged
→ / space: next · ←: back · Home/End: jump · Ctrl+P → PDF