Loops and Conditionals in Python
The counter belongs before the loop because it represents the total collected across all iterations.
From grep to a Python loop
The Unix grep command can be understood as a small sequence of programming actions: receive a pattern, open a file, inspect each line, decide whether the line matches, count successful matches, and report the final count. A Python grep simulator is useful because it makes the loop and conditional decision visible. The loop visits every line, while the conditional determines whether the counter changes.
The counter belongs before the loop because it represents the total collected across all iterations.
The execution flow
The simulator follows one repeating path. It obtains a line from the file, tests that line against the user-supplied regular expression, and then takes one of two branches. If the pattern matches, the counter increases by one. If it does not match, the counter remains unchanged. After the loop has processed the final line, the program reports the counter.
A five-line trace
Counting lines that start with X
Trace a search for the pattern ^X through five file lines.
- 1Before iteration
The counter starts at 0.
- 2Author: John
The line does not match ^X, so the counter stays 0.
- 3X-Priority: 1
The line matches ^X, so the counter becomes 1.
- 4Subject: Meeting
The line does not match ^X, so the counter stays 1.
- 5X-Mailer: Outlook
The line matches ^X, so the counter becomes 2.
- 6Date: Monday
The line does not match ^X, so the counter stays 2.
- 7After iteration
The loop ends after all five lines have been tested.
Two lines match ^X.
Building the simulator
The implementation mirrors the trace. The pattern and filename are supplied by the user. The file is opened, the counter is initialized before iteration begins, and each line is tested inside the loop. The counter is reported only after the loop has finished.
import re
pattern = input("Pattern: ")
filename = input("Filename: ")
count = 0
with open(filename) as file:
for line in file:
line = line.strip()
if re.search(pattern, line):
count += 1
print(count)- 1The program imports the regular-expression module.
- 2It asks for a pattern and a filename.
- 3It sets count to 0 before reading lines.
- 4For each line, it removes the line-ending characters with strip.
- 5The conditional tests the current line with the supplied pattern.
- 6A match adds one to count; a non-match leaves count unchanged.
- 7The program reports the accumulated count after all lines have been processed.
- The pattern is supplied by the user.
- The filename identifies the file to inspect.
- The counter is initialized before the loop.
- The loop processes each line from the file.
- The line is stripped before the regex test.
- The counter increases only when
re.searchfinds a match. - The final count is printed after the loop.
Tracing before debugging
When a count looks wrong, trace the execution state instead of looking only at the final number. For every iteration, record the current line, whether the regular expression matches, and the counter after the conditional. This exposes whether the loop is visiting the expected lines and whether the counter changes only on matches.
| Trace question | What to inspect |
|---|---|
| Which input is being tested? | The current line from the file |
| What decision was made? | Whether the line matches the supplied pattern |
| What changed? | The counter increases only after a match |
| When is the result reported? | After the loop ends |
A compact checklist for tracing one iteration at a time
Mistakes in line matching
Common mistake Initializing the counter inside the loop
What it looks likeThe counter is reset each time another line is read.
Why it failsEarlier matches are discarded on every iteration.
Do this insteadInitialize the counter before the loop so it can accumulate matches across the entire file.
Common mistake Forgetting to strip the newline character
What it looks likeThe line is tested with its line-ending character still attached.
Why it failsThe text being tested is not exactly the visible line content.
Do this insteadStrip each line before applying the regular expression.
Common mistake Using re.match() instead of re.search()
What it looks likeThe program uses re.match when the simulator should use the matching behavior described for re.search.
Why it failsThe source identifies this as a common mistake when implementing the simulator.
Do this insteadUse re.search for the line test in this grep-style program.
Common mistake Reporting the count inside the loop
What it looks likeThe program reports a result before every remaining line has been tested.
Why it failsThe reported value is not yet the total for the file.
Do this insteadReport the count after the loop ends.
Common mistake Testing only the first few lines
What it looks likeThe loop stops before every line has been inspected.
Why it failsMatches later in the file are never included.
Do this insteadAllow the file iteration to test every line.
| Correct structure | Mistaken structure |
|---|---|
| Counter before the loop | Counter reset inside the loop |
| Test the current line | Test the wrong input |
| Count only after a match | Change the count on every iteration |
| Report after all lines | Report before iteration is complete |
Structural contrasts that affect the final match count
Patterns beyond a prefix
The same loop can search for more complex line formats. Instead of looking only for lines beginning with X, the pattern can describe a structure such as a line containing New Revision: followed by a number. The loop, conditional, counter, and final report remain the same; only the supplied regular expression changes.
Write or adapt the simulator so that it searches a file for lines matching a pattern describing New Revision: followed by a number. Then trace at least three iterations by recording the current line, match result, and counter.
Hints
- Keep the counter initialization before the loop.
- Apply the pattern to every line, not just the first few.
- Check the counter after each conditional decision.
- Report the final count only after the loop ends.
What do you think happens?
A five-line file contains two lines beginning with X. If the counter starts at 0 and increases only when the pattern ^X matches, what should the final count be?
Reveal answer
2
The source trace shows two matching lines: X-Priority: 1 and X-Mailer: Outlook. The other three lines leave the counter unchanged.
The complete mental model
- 1A grep simulator reads a file line by line and tests every line against a user-supplied regular expression.
- 2Initialize the match counter before the loop.
- 3Use the conditional to increment the counter only when the current line matches.
- 4Strip each line before testing it and use
re.searchrather thanre.matchfor the simulator described here. - 5Trace the current line, match result, and counter to debug the program.
Key Takeaways
- The program's loop processes every file line, and its conditional decides whether that line contributes to the count.
- The counter must be initialized before iteration and reported after iteration.
- Tracing each line and counter value makes regex-based matching errors easier to locate.
- Common implementation problems include resetting the counter, retaining newline characters, using
re.matchinstead ofre.search, and stopping or reporting too early.