Your CMO just pulled up the Q2 report. She's not interested in every campaign — she wants the first one that blew past the CPL target. Where do you even start?
compute_cpl_per_campaign already gave me CPL for the whole list. But now I'd need to scroll top to bottom, check each row with an IF, and stop the moment I spotted one — thirty rows, thirty clicks.
That scroll is a while loop. A while loop runs as long as a condition is true, and you control the counter yourself. This is the key difference from a for loop — for visits every item; while stops when you say stop:
i = 0
while i < len(campaigns):
campaign = campaigns[i]
if campaign["cpl"] > target_cpl:
break # stop the moment we find one
i += 1 # advance the counter — forget this and it runs foreverWhy not just use a for loop with an if? I could loop all campaigns, check each CPL, and return the first match. Feels simpler.
You could — and a for with an early return works fine. But break makes the intent explicit at a glance: "I am done the moment I have my answer — stop searching." With while + break, any reader sees the sentinel pattern immediately without tracing a return buried mid-loop.
Oh — so while + break is the idiom that advertises "I exit early on purpose," not just as a side effect.
Exactly. And the full function is just that loop wrapped in a def, with an i counter as your row pointer:
def find_first_over_target(campaigns: list, target_cpl: float) -> dict:
i = 0
while i < len(campaigns):
campaign = campaigns[i]
if campaign["cpl"] > target_cpl:
print(f"Found: {campaign['name']} CPL {campaign['cpl']} > target {target_cpl}")
return campaign
i += 1
print("No campaign exceeded target CPL")
return {}return {} is the "no guilty campaigns" verdict. My CMO will either be relieved or suspicious.
The caller checks if result: and branches — clean contract. One last trap: forget i += 1 and the condition i < len(campaigns) never changes. The loop runs forever. The increment is the engine; without it you've built an infinite scroll with no way out.
A while loop runs until its condition becomes False — or until break exits it early.
| Keyword | Purpose |
|---|---|
while condition: | Keep looping while condition holds |
i += 1 | Advance the counter — required to avoid infinite loop |
break | Exit immediately, wherever you are in the body |
continue | Skip the rest of this iteration, jump to next |
i = 0
while i < len(items):
if items[i] matches condition:
return items[i] # or break + handle outside
i += 1
return fallback
Use while + break when the goal is "stop as soon as the answer is found" — it signals intent more clearly than a for loop with a mid-body return.
Your CMO just pulled up the Q2 report. She's not interested in every campaign — she wants the first one that blew past the CPL target. Where do you even start?
compute_cpl_per_campaign already gave me CPL for the whole list. But now I'd need to scroll top to bottom, check each row with an IF, and stop the moment I spotted one — thirty rows, thirty clicks.
That scroll is a while loop. A while loop runs as long as a condition is true, and you control the counter yourself. This is the key difference from a for loop — for visits every item; while stops when you say stop:
i = 0
while i < len(campaigns):
campaign = campaigns[i]
if campaign["cpl"] > target_cpl:
break # stop the moment we find one
i += 1 # advance the counter — forget this and it runs foreverWhy not just use a for loop with an if? I could loop all campaigns, check each CPL, and return the first match. Feels simpler.
You could — and a for with an early return works fine. But break makes the intent explicit at a glance: "I am done the moment I have my answer — stop searching." With while + break, any reader sees the sentinel pattern immediately without tracing a return buried mid-loop.
Oh — so while + break is the idiom that advertises "I exit early on purpose," not just as a side effect.
Exactly. And the full function is just that loop wrapped in a def, with an i counter as your row pointer:
def find_first_over_target(campaigns: list, target_cpl: float) -> dict:
i = 0
while i < len(campaigns):
campaign = campaigns[i]
if campaign["cpl"] > target_cpl:
print(f"Found: {campaign['name']} CPL {campaign['cpl']} > target {target_cpl}")
return campaign
i += 1
print("No campaign exceeded target CPL")
return {}return {} is the "no guilty campaigns" verdict. My CMO will either be relieved or suspicious.
The caller checks if result: and branches — clean contract. One last trap: forget i += 1 and the condition i < len(campaigns) never changes. The loop runs forever. The increment is the engine; without it you've built an infinite scroll with no way out.
A while loop runs until its condition becomes False — or until break exits it early.
| Keyword | Purpose |
|---|---|
while condition: | Keep looping while condition holds |
i += 1 | Advance the counter — required to avoid infinite loop |
break | Exit immediately, wherever you are in the body |
continue | Skip the rest of this iteration, jump to next |
i = 0
while i < len(items):
if items[i] matches condition:
return items[i] # or break + handle outside
i += 1
return fallback
Use while + break when the goal is "stop as soon as the answer is found" — it signals intent more clearly than a for loop with a mid-body return.
Isla's CMO wants to flag the earliest-launched campaign in Q2 that has already blown past the CPL target — list order represents launch order, so the first match matters. Write `find_first_over_target(campaigns, target_cpl)` that takes a list of campaign dicts (each with `"name"` and `"cpl"` keys) and a float target, and returns the first campaign dict whose CPL strictly exceeds `target_cpl`. Return `{}` if no campaign exceeds the target.
Tap each step for scaffolded hints.
No blank-editor panic.