Case-based lecture: you attempt, we discuss, then we reveal.
cs-053, cs-054, cs-138, cs-055, cs-056Write 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.
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.
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.
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.
Three validation checks (email-ish, phone-ish, id format) become three functions plus a driver. Naming, single purpose, return vs print.
**Input:** values: "ada@uni.edu" (ok-ish), "07123 456789", "S2023/4471"**Output:** three independent verdict functions + a driver printing a tableSolve for the worked instance first. Then ask what would change if onenumber, name, or condition were replaced by something stranger.Pseudocode, flowchart or Python. State one assumption. Compare with your neighbour at minute 4.
Instructor: the reveal, the reasoning section and common wrong turns are in the instructor distribution for cs-053 — not embedded in this deck.
One 40-line script computes grade statistics. Decompose it into functions without changing behaviour, then defend the boundaries chosen.
**Input:** one script: parse score lines, drop missing, average per student, print report**Output:** functions parse_line, averages, format_report - behaviour unchanged; test by identical outputSolve for the worked instance first. Then ask what would change if onenumber, name, or condition were replaced by something stranger.Pseudocode, flowchart or Python. State one assumption. Compare with your neighbour at minute 4.
Instructor: the reveal, the reasoning section and common wrong turns are in the instructor distribution for cs-054 — not embedded in this deck.
The IT helpdesk logged last week's outages, one line per incident:
**Input:** the eight-row incident log**Output:** per-system minutes (W1 90 raw vs 80 merged, W2 80, DB55) — cause table — one flagged system — one logging questionSolve for the worked instance first. Then ask what would change if onePseudocode, flowchart or Python. State one assumption. Compare with your neighbour at minute 4.
Instructor: the reveal, the reasoning section and common wrong turns are in the instructor distribution for cs-138 — not embedded in this deck.
A function with a mutable default argument accumulates state across calls. Diagnose, fix, and generalise the lesson to API design.
**Input:** def add_tag(tag, tags=[]): called three times**Output:** the default list persists: calls accumulate; fix: None sentinel + fresh listSolve for the worked instance first. Then ask what would change if onenumber, name, or condition were replaced by something stranger.Pseudocode, flowchart or Python. State one assumption. Compare with your neighbour at minute 4.
Instructor: the reveal, the reasoning section and common wrong turns are in the instructor distribution for cs-055 — not embedded in this deck.
Build a text pipeline (clean, tokenise, count, report) where each stage is a function returning the next stage's input. Composition and contracts.
**Input:** text ' the CAT, the dog! '**Output:** clean -> 'the CAT, the dog!'; tokenise -> ['the','CAT','the','dog']; count -> the:2, dog:1, cat:1Solve for the worked instance first. Then ask what would change if onenumber, name, or condition were replaced by something stranger.Pseudocode, flowchart or Python. State one assumption. Compare with your neighbour at minute 4.
Instructor: the reveal, the reasoning section and common wrong turns are in the instructor distribution for cs-056 — not embedded in this deck.