Skip to content
The AI Agent Factory

13.4 Decisions and repetition

Approved by
Panaversity ·
11 min read

In everyday life. A mail clerk sorts the morning mail. Each envelope stamped BILL goes in the AP tray. Every other envelope goes in the general tray, even a bill that nobody stamped. The clerk repeats one small decision for every envelope. That is most of what a program does too.

What you will learn. How to find each decision and each repeated step, and say it as a sentence.

Why it matters. Each decision is a rule, and an empty value can slip through one.

A condition is a yes-or-no question about a value, such as whether the status is open. An if runs some lines only when its condition is true, and an else runs others when it is false. A loop repeats lines once for each item in a collection. Two words join conditions. With and, both must be true. With or, either one is enough, and a or b gives b when a is empty.

ShapeReads asBrightline use
if ... else ...Do this, or thatAn open invoice is listed, a paid one is skipped
A loop over itemsFor each invoice, do thisPrint one line per invoice
A filterKeep only the items whose answer is yesKeep the open invoices
a or bUse a, or use b if a is emptyStated due date, or the one computed from the terms
whileRepeat while a condition stays trueFetch pages of invoices until one comes back empty

How do I find each decision?

Look for every place where the code chooses: an if, a filter, or an or.

At work. Ana's Chapter 12 script does three things in a row for every invoice. It skips the invoice unless it is open. It takes the invoice's due date, or computes one from the terms. And it keeps the invoice if the due date is on or before Friday. Reading the script means finding those three decisions and checking each one against the rule. The agent's first plan in Chapter 12 missed the second one.

The plainest decision is an if. Here is the mail clerk's rule, in both languages. === in TypeScript and == in Python ask whether two values are equal. Predict what each prints.

const stamp = "BILL";
if (stamp === "BILL") {
  console.log("AP tray");
} else {
  console.log("general tray");
}
stamp = "BILL"
if stamp == "BILL":
    print("AP tray")
else:
    print("general tray")

Both print AP tray. With any other stamp, both print general tray.

A filter asks the same question of every item. In TypeScript, each invoice is written in braces, as names with their values. The filter keeps each invoice, inv, whose status is "open". Its rule is written in place with an arrow: (inv) => ... means "for each inv, the answer is what follows the arrow". || is TypeScript's or. Boolean(...) shows whether a value counts as true or false. Predict both lines of the loop, then the last line.

const invoices = [
  { number: "INV-8301", status: "open", due: "2027-03-03" },
  { number: "INV-8309", status: "paid", due: "2027-02-28" },
  { number: "INV-8305", status: "open", due: "" },
];
const open = invoices.filter((inv) => inv.status === "open");
for (const inv of open) {
  const due = inv.due || "from the terms";
  console.log(inv.number, due);
}
console.log(Boolean(""), Boolean([]));
INV-8301 2027-03-03
INV-8305 from the terms
false true

In Python. The filter here is a list comprehension: a list built from another list in one line, read as "each inv in invoices, if its status is open". The label list[dict[str, str]] says it is a list of records, each with text names and text values.

invoices: list[dict[str, str]] = [
    {"number": "INV-8301", "status": "open", "due": "2027-03-03"},
    {"number": "INV-8309", "status": "paid", "due": "2027-02-28"},
    {"number": "INV-8305", "status": "open", "due": ""},
]
open_invoices = [inv for inv in invoices if inv["status"] == "open"]
for inv in open_invoices:
    due = inv["due"] or "from the terms"
    print(inv["number"], due)
print(bool(""), bool([]))
INV-8301 2027-03-03
INV-8305 from the terms
False False

Why can an empty value slip through a decision?

Some values count as false, and not all of them are empty. In a condition, both languages treat some values as false even though they are not false: the number 0, empty text "", and "nothing". That is why due || computed works. An empty due date counts as false, so the computed one is used. It is also a trap. An amount of 0 counts as false too, so a condition meant to ask "is there an amount?" would treat a zero-dollar credit as missing.

How does a loop stop?

A for loop runs once for each item you already have. A while loop repeats as long as its condition stays true. It is for work where you do not know the count in advance, like a clerk opening mailbags until one comes empty, or reading pages of invoices until one comes back empty. Every while loop needs a way to stop. That is a line that changes something its condition checks, or a break or return that leaves the loop. Without one, the loop never ends. pages is a list of lists, one for each page, and pages[page] is the one at that position. let makes a variable that can change, and .length in TypeScript and len() in Python count a list's items. Predict the last line.

In TypeScript.

const pages = [["INV-8301", "INV-8302"], ["INV-8306"], []];
let page = 0;
while (pages[page].length > 0) {
  console.log("page", page + 1, "has", pages[page].length);
  page += 1;
}
console.log("stopped at page", page + 1);

In Python.

pages = [["INV-8301", "INV-8302"], ["INV-8306"], []]
page = 0
while len(pages[page]) > 0:
    print("page", page + 1, "has", len(pages[page]))
    page += 1
print("stopped at page", page + 1)

Both print:

page 1 has 2
page 2 has 1
stopped at page 3

page += 1 adds 1 to page, and it is the line that moves the loop toward its end. Positions count from 0, so the code prints page + 1.

Figure 13.4. A while loop reading pages. Page 1 holds INV-8301 and INV-8302 and page 2 holds INV-8306, so each time the loop prints and moves on with page += 1. Page 3 is empty, so the condition is false and the loop stops. Without page += 1, it would fetch the same page again and again.

Figure 13.4. A while loop. Each pass checks the condition again, and page += 1 is what moves it toward the empty page that stops it.

Where the languages differ.

TypeScriptPython
Equal=== (always use this one, not ==)==
And, or, not&&, `
An empty list in a conditionCounts as trueCounts as false
Repeat while truewhile (cond) { ... }while cond:

The row about an empty list causes real bugs. "If there are any errors, stop" written as if (errors) in TypeScript is always true, even when the list of errors is empty. Check the length instead: errors.length > 0.

The operating rule: find every decision in the code and say it as a sentence, then check each sentence against the business rule, including what happens to empty values. For every while loop, find the line that makes it stop.

Figure 13.5. Four invoice rows through a filter for Friday, March 5, 2027. INV-8301 is open and due March 3, so it stays. INV-8309 is paid, so it leaves at the first check. INV-8305 has no due date, so Net 30 gives March 5, and it stays. INV-8303 is due March 6, after Friday, so it leaves.

Figure 13.5. A row through a filter. Each gate is one condition.

Practice it. Practice: decisions and loops, in your own tool, ten minutes.

Check yourself

Question 1 / 8

0 answered

The invoice loop uses inv.due || "from the terms". What does it print for INV-8305, whose due is empty?

Owner
Panaversity