047. Break
Exit a loop early when a condition is met
047. Break
RAM Manager: Find the First Offender
Aryan’s RAM manager scans the process list to find the first process exceeding a memory threshold. Once found, there’s no point scanning the rest.
# Without break — scans everything even after finding it
def find_hog(processes, threshold_mb):
found = None
for p in processes:
if p["mem_mb"] > threshold_mb:
found = p # keeps overwriting — returns LAST match, not first
return found
# With early return — stops at first match
def find_hog(processes, threshold_mb):
for p in processes:
if p["mem_mb"] > threshold_mb:
return p # first match, done
return NoneThe broken code has exactly this bug — first_index returns the LAST occurrence because it overwrites found_idx on every match instead of stopping.
💡 Fun fact: The “early exit” pattern that break enables is the basis of many classic algorithms. Binary search exits the loop as soon as the target is found. Linear search — exactly what Aryan is writing — exits at the first match. These early-exit optimizations can reduce average runtime from O(n) to O(n/2) with zero extra memory, which is why they appear in performance-critical systems from database engines to CPU branch predictors.
⚠️ Watch out: break only exits the innermost loop. In nested loops, break in the inner loop leaves the outer loop still running. Beginners trying to escape two levels of nesting are surprised when their code keeps looping after the break. The clean solution is to use return inside a function, which exits all loops at once.
🤔 Think about it: first_index without break returns the last occurrence; with break (or early return) it returns the first. Can you think of a real-world situation where you’d actually want the last occurrence — and how would you write that efficiently without scanning the entire list twice?
Learning objectives
- Use break to exit a loop when a condition is met
- Use return inside a loop for early exit from a function
- Understand the difference between finding first vs last occurrence
Key concepts
- break
- early exit
- search
Try it
Concept detail
break exits the innermost loop immediately, skipping all remaining iterations.
Used when: you’ve found what you’re looking for and further work is wasteful.
Two patterns for early exit:
Pattern 1: break + result variable found = None for item in items: if match(item): found = item break # exits loop
found is set if we found something, None if not
Pattern 2: return inside loop (cleaner in functions) for item in items: if match(item): return item # exits loop AND function return None # only reached if nothing matched
The return pattern is more common in Python — no flag variable needed.
Performance: the broken first_index scans ALL n items every time. With early return: best case O(1) if match is at index 0, average O(n/2).
break only exits ONE loop level — for nested loops, break exits only the inner loop. To exit multiple levels, use a function with return, or set a flag.
Solution
def first_index(items, target):
for i, item in enumerate(items):
if item == target:
return i
return -1
def has_negative(numbers):
for n in numbers:
if n < 0:
return True
return FalseTests
def test_first_index_found():
# [10, 20, 30, 20] — 20 appears at index 1 AND 3
# broken code returns 3 (last), correct code returns 1 (first)
assert first_index([10, 20, 30, 20], 20) == 1
def test_first_not_last():
result = first_index([5, 5, 5], 5)
assert result == 0, f"Got index {result} — must return FIRST occurrence, not last"
def test_first_index_not_found():
assert first_index([1, 2, 3], 99) == -1
def test_first_index_first_element():
assert first_index([5, 1, 2], 5) == 0
def test_has_negative_true():
assert has_negative([1, 2, -3, 4]) == True
def test_has_negative_false():
assert has_negative([1, 2, 3]) == False
def test_has_negative_empty():
assert has_negative([]) == False