Chapter 05 · Week 5

Repetition II — Functions

"Come now, let us reason together, says the LORD." — Isaiah 1:18
5

Use a real reasoning tool first

Bayes' theorem is the most-used reasoning function in apologetics — and the one whose intuitions surprise people most. Play with it before you write one.

Bayesian Update — How Evidence Shifts Belief

Bayes' theorem turns a prior probability into a posterior probability when new evidence arrives. The math is small. The intuitions it overturns are not. Move the sliders — or try a preset — and watch what the formula says.

Scenarios:
0.500
0.900
0.100
PRIOR
50.0%
POSTERIOR
90.0%
+40.0 percentage points
Show the formula
 

The denominator is the total probability of observing the evidence, summed over both worlds (H true and H false), weighted by the prior. Bayes is just "weighted average" — the weights are the priors.

Counterintuitive scenario to try: Click "Rare disease test." A 99% accurate test on a disease that's only 1% prevalent gives a positive result. Most people guess the person almost certainly has the disease. Bayes says ~50%. Watch why.

Why This Matters

Last chapter you learned to make the computer do the same thing many times. This chapter you learn to make the programmer — that's you — write the same thing only once.

The cost of repeating yourself in code is enormous and quiet. When you write the same logic in three places and a bug shows up in one of them, the bug is also lurking in the other two. The fix is functions — a chunk of code with a name, written once, used anywhere.

The Christian intellectual tradition has worked the same way for centuries. Aquinas didn't reinvent the Cosmological Argument every time. Pascal didn't write the wager fresh in each Pensée. Once an argument is carefully built, reusing it is a virtue, not laziness.

The Anatomy of a Function

double bayes_update(double prior, double L_H, double L_notH) {
    double num = L_H * prior;
    double den = num + L_notH * (1.0 - prior);
    return num / den;
}
  • double — what the function returns.
  • bayes_update — the function's name. Verb phrases for functions; nouns for variables.
  • (double prior, double L_H, double L_notH) — parameters. These are local variables that exist only inside the function.
  • return num / den; — sends a value back to whoever called the function.

Pass by Value vs. Pass by Reference

By default, function parameters are copies of what you passed in. Modifying them inside the function doesn't affect the caller:

void try_to_double(int x) {
    x = x * 2;     // modifies local copy only
}
int n = 5;
try_to_double(n);
cout << n;   // still 5, not 10

To let a function modify the caller's variable, mark the parameter with &:

void actually_double(int& x) {
    x = x * 2;     // modifies the caller's variable
}

Use pass-by-reference when you want the function to modify state in the caller, or for large objects where copying would be expensive. Use pass-by-value (the default) almost everywhere else.

Composition

Functions can call other functions. This is where the power compounds:

double chained_bayes(double prior,
                     double L1_H, double L1_notH,
                     double L2_H, double L2_notH) {
    double first  = bayes_update(prior, L1_H, L1_notH);
    return bayes_update(first, L2_H, L2_notH);
}

chained_bayes applies Bayes twice — once with the first piece of evidence, once with the second. The actual math lives in bayes_update, in one place. The bigger function just composes the smaller one. This is the move that distinguishes working programmers from beginners.

Coach's Note — Beginners write one giant function that does everything. Working programmers write twenty small functions, each doing one thing, that combine. The latter is easier to read, easier to debug, easier to change.

This Week's Project

You're ready for Project 5: Reasoning Toolkit. A menu-driven program with five named operations — Bayesian update, modus ponens, complement, conjunction, validity — each its own function. main is just the menu loop and dispatcher.

Check Your Reps

Functions — Quick Check

Question 1 of 4
What does n print?
void doubleIt(int x) {
    x = x * 2;
}

int main() {
    int n = 5;
    doubleIt(n);
    cout << n;
    return 0;
}
Why: The parameter x is passed by value — it's a copy. Modifying the copy doesn't touch the caller's n. To actually double n, change the parameter to int& x.
Question 2 of 4
A rare disease affects 1% of people. A test is 99% accurate both ways (P(E|H) = 0.99, P(E|¬H) = 0.01). A random person tests positive. About what's the posterior probability they actually have the disease?
Why: This is the classic Base Rate Fallacy. Most people intuit ~99% because the test is 99% accurate. Bayes says: (0.99 × 0.01) / (0.99 × 0.01 + 0.01 × 0.99) = 0.0099 / 0.0198 = 0.5. The false positives from the healthy 99% match the true positives from the sick 1%. Try it in the widget above.
Question 3 of 4
Which is the right name for a function that returns a boolean indicating "is this number prime"?
Why: Functions that return a boolean are conventionally named as questions: is*, has*, can*. They read like the condition they're used in: if (isPrime(n)) { ... }.
Question 4 of 4
Why does chained_bayes call bayes_update twice instead of inlining the math?
Why: Composition is the deeper move: write small, trustworthy primitives and build on them. If bayes_update has a bug, you fix it in one place. If chained_bayes duplicated the math, the bug would live in two places — and you'd inevitably miss one.
YOU FINISHED. NICE WORK.

← WEEK 4: LOOPS   ·   WEEK 6: COLLECTIONS →