A field guide for two very different machines

The Console
& the Matrix

One is for telling your Mac what to do in plain-ish English. The other is for telling mathematics what to do in vectors and matrices. This page teaches both — from first principles to working code — and proves why the numerical methods actually work.

AppleScript · automation & the Open Scripting Architecture GNU Octave · numerical computing, MATLAB-compatible 4 worked proofs 6 live sandboxes
00 — Orientation

Two languages, one page

It is unusual to learn AppleScript and GNU Octave side by side, because they answer opposite questions. AppleScript answers "how do I make my computer do a chore for me?" — open apps, rename files, click buttons, glue programs together. Octave answers "how do I compute this number?" — multiply matrices, solve equations, integrate functions, plot results.

Studying them together is a good way to feel the full spread of what "programming" means. One reads almost like a sentence; the other reads like a page of mathematics. Throughout, AppleScript is marked in amber and Octave in teal. The proofs at the end are marked in gold — they justify the numerical recipes you will have just used.

How to use this page The code blocks are real and correct. AppleScript runs only on macOS (paste into Script Editor), so its "live" section simulates what a dialog would look like. Every Octave concept in the live section is re-implemented in JavaScript so it runs right here in your browser — the math is identical even though the engine differs.
01 — AppleScript

What AppleScript is

AppleScript is Apple's scripting language for automating applications. It was introduced in 1993 and sits on top of a system called the Open Scripting Architecture. The core idea: applications publish a "dictionary" of things they can do (a Finder can move files, Safari can return the URL of a tab), and your script sends those commands as Apple Events.

Its defining trait is an English-like syntax. Where Python says finder.trash(file), AppleScript says move file to trash. This makes simple scripts wonderfully readable and complicated scripts occasionally maddening, because the same idea can be phrased several ways.

  • Runs on macOS only — author it in Script Editor (/Applications/Utilities) or run from the terminal with osascript.
  • Best at gluing apps together: Finder, Mail, Safari, Calendar, Music, Photos, Microsoft Office, and anything "scriptable."
  • Can drop down to the Unix shell with do shell script, and can drive any app's menus/buttons through System Events (UI scripting).
02 — AppleScript

Syntax & variables

Statements are usually one per line. Comments use -- for a line or (* … *) for a block. You create a variable with set … to …, and you join text with the & operator.

hello.applescript AppleScript
-- a line comment
(* a block comment:
   spans many lines *)

set greeting to "Hello"
set who to "world"
set message to greeting & ", " & who & "!"

display dialog message          -- pops a window
return message                      --> "Hello, world!"

A handful of things to internalise early:

  • No semicolons. Line breaks end statements. Use ¬ (Option-L) to continue a long line.
  • Assignment is set x to v, not x = v. You read a value back with get (often implicit).
  • The result of the last expression is shown in Script Editor's result pane; return hands a value back from a script or handler.
  • Equality is = or the words is equal to; inequality is or is not equal to.
03 — AppleScript

Data types you'll actually use

AppleScript's everyday types are text, integer, real, boolean, date, list, and record. Lists are ordered (1-indexed!) collections; records are key–value bundles. Converting between types is called coercion and uses the word as.

types.applescript AppleScript
-- LISTS are 1-indexed, ordered
set primes to {2, 3, 5, 7, 11}
set first to item 1 of primes     --> 2
set last  to last item of primes    --> 11
set howMany to count of primes       --> 5
set end of primes to 13            -- append → {2,3,5,7,11,13}

-- RECORDS are key:value (unordered)
set person to {name:"Ada", born:1815, active:true}
set n to name of person             --> "Ada"

-- COERCION with `as`
set x to "42" as integer            --> 42  (text → integer)
set s to 3.14 as text              --> "3.14"
set joined to {"a", "b", "c"} as text  -- uses text item delimiters

Splitting & joining text — the delimiter trick

AppleScript has no split() function. Instead you temporarily set text item delimiters, then coerce. It's the one idiom every AppleScripter memorises.

split-join.applescript AppleScript
set csv to "red,green,blue"

-- SPLIT: set delimiter, read text items
set AppleScript's text item delimiters to ","
set parts to text items of csv   --> {"red","green","blue"}

-- JOIN: set delimiter, coerce list → text
set AppleScript's text item delimiters to " | "
set joined to parts as text     --> "red | green | blue"

-- ALWAYS reset delimiters afterwards (they're global!)
set AppleScript's text item delimiters to ""
Gotcha Text item delimiters are a global setting. If you forget to reset them, unrelated text coercions later in your script will behave strangely. Save the old value and restore it.
04 — AppleScript

Control flow

Conditionals are if … then … else … end if. Loops are all spelled repeat, with several flavours.

flow.applescript AppleScript
set score to 82

if score  90 then
    set grade to "A"
else if score  80 then
    set grade to "B"
else
    set grade to "C or below"
end if

-- repeat a fixed number of times
repeat 3 times
    beep
end repeat

-- counted loop
set total to 0
repeat with i from 1 to 100
    set total to total + i
end repeat                              -- total = 5050

-- iterate over a list
repeat with fruit in {"apple", "pear", "plum"}
    log (fruit as text)        -- log → Script Editor's log pane
end repeat

-- conditional loop
set n to 10
repeat while n > 0
    set n to n - 1
end repeat

Boolean operators are the words and, or, not. Strings have friendly tests too: contains, starts with, ends with.

05 — AppleScript

Handlers (functions)

A reusable function is called a handler. Define it with on name(params) … end name and call it normally. Handlers are how you keep scripts tidy.

handlers.applescript AppleScript
-- positional parameters
on circleArea(r)
    return 3.14159265 * r * r
end circleArea

set a to circleArea(5)            --> 78.539816...

-- a handler that filters a list
on evensIn(aList)
    set out to {}
    repeat with x in aList
        if (x mod 2) = 0 then set end of out to (x as integer)
    end repeat
    return out
end evensIn

evensIn({1, 2, 3, 4, 5, 6})         --> {2, 4, 6}

-- labeled parameters read like English
on greet someone given loudly:isLoud
    set msg to "Hi " & someone
    if isLoud then set msg to msg & "!!!"
    return msg
end greet

greet "Grace" given loudly:true     --> "Hi Grace!!!"
06 — AppleScript

The whole point: driving applications

You command an app inside a tell application "Name" … end tell block. Inside it, the app's own vocabulary becomes available.

driving-apps.applescript AppleScript
-- Ask Finder how many items are on the desktop
tell application "Finder"
    set n to count (every item of desktop)
    set whereAmI to name of home
end tell

-- Get the URL of Safari's front tab
tell application "Safari"
    set u to URL of current tab of front window
end tell

-- Speak text and put something on the clipboard
say "Build complete" using "Samantha"
set the clipboard to "copied by a script"

-- UI scripting: click a menu when an app isn't scriptable
tell application "System Events"
    tell process "TextEdit"
        click menu item "New" of menu "File" of menu bar 1
    end tell
end tell
Discovery To see what an app can do, open Script Editor → File → Open Dictionary… and pick the app. That dictionary is the authoritative list of nouns and verbs it understands.
07 — AppleScript

Errors & the Unix escape hatch

Wrap risky code in try … on error … end try. And when AppleScript can't do something directly, do shell script runs any command-line program and returns its output as text.

errors-shell.applescript AppleScript
try
    set x to "not a number" as integer   -- this fails
on error errMsg number errNum
    display dialog "Failed: " & errMsg & " (#" & errNum & ")"
end try

-- Borrow the power of the shell
set today to do shell script "date +%Y-%m-%d"
set files to do shell script "ls -1 ~/Documents | wc -l"
display dialog "On " & today & " you have " & files & " documents."
08 — AppleScript · Interactive

Live: build a dialog

AppleScript needs macOS, so we can't run it in a browser. But its single most common command — display dialog — produces a predictable window. Compose one below and press Run to see a faithful simulation of what macOS would show, alongside the exact AppleScript that produces it.

display dialog — simulator

simulated · macOS
09 — GNU Octave

What GNU Octave is

GNU Octave is a free, open-source language for numerical computation. It is largely compatible with MATLAB, so the same scripts often run in both. Its native object is not a scalar but a matrix — everything is an array, and a plain number is just a 1×1 matrix. That single design choice makes linear algebra, statistics, and signal processing feel effortless.

  • Comments use % or #. A trailing ; suppresses the automatic echo of a result.
  • Cross-platform (macOS, Linux, Windows). Run interactively, or as scripts with octave file.m.
  • Batteries included: linear algebra, root-finding, integration, ODEs, FFTs, optimisation, plotting.
10 — GNU Octave

Vectors & matrices

Spaces or commas separate columns; semicolons separate rows. Ranges and linspace build vectors without typing every element.

arrays.m Octave
v = [1 2 3 4];          % a row vector
c = [1; 2; 3];           % a column vector
A = [1 2; 3 4];          % a 2x2 matrix

r = 0:2:10;             % 0 2 4 6 8 10  (start:step:stop)
x = linspace(0, 1, 5);    % 0 0.25 0.5 0.75 1

size(A)                  % ans = 2   2
length(v)                % ans = 4
A(2, 1)                % ans = 3   (row 2, col 1 — 1-indexed!)
A(:, 1)                 % first column → [1; 3]

zeros(2,3), ones(2), eye(3)   % zeros, ones, identity
Mental model In Octave, indexing starts at 1 (like AppleScript, unlike Python/C). A(i, j) is row i, column j. A lone colon : means "all of this dimension."
11 — GNU Octave

Operations: the dot is everything

This is the concept that trips up every newcomer. * means matrix multiplication. Put a dot in front — .*, ./, .^ — and it becomes element-by-element.

dot-ops.m Octave
A = [1 2; 3 4];
B = [5 6; 7 8];

A * B        % MATRIX product → [19 22; 43 50]
A .* B       % ELEMENT product → [5 12; 21 32]

v = [1 2 3];
v .^ 2       % [1 4 9]   (square each element)
A'            % transpose → [1 3; 2 4]

% reductions collapse a matrix to numbers
sum(v)        % 6
mean(v)       % 2
max(v)        % 3
cumsum(v)     % [1 3 6]

% logical indexing — keep elements that pass a test
w = [-2 5 -1 8 0];
w(w > 0)     % [5 8]  — selects positive entries
The #1 beginner bug Writing v^2 on a vector throws an error (it tries a matrix power and needs a square matrix). You almost always want v.^2. When in doubt, add the dot.
12 — GNU Octave

Control flow & functions

Blocks close with end (or the explicit endif/endfor/endfunction). Anonymous functions with @(args) are the workhorse for passing math around.

functions.m Octave
% conditionals
t = 37.5;
if t > 38
  disp("fever")
elseif t >= 37
  disp("elevated")
else
  disp("normal")
end

% a for-loop building a running sum
s = 0;
for k = 1:100
  s = s + k;
end
printf("sum = %d\n", s);     % sum = 5050

% ...but vectorise! same result, no loop:
s2 = sum(1:100);              % 5050

% anonymous function (a closure)
f = @(x) x.^2 - 2;
f(3)                          % 7
f([1 2 3])                    % [-1 2 7]  (works on vectors)

% a named function
function y = hypotenuse(a, b)
  y = sqrt(a.^2 + b.^2);
endfunction
hypotenuse(3, 4)               % 5

Prefer vectorisation over loops where you can — it is shorter and dramatically faster, because the work happens in compiled array routines instead of the interpreter.

13 — GNU Octave

Linear algebra in one line

This is where Octave shines. Solving \(Ax = b\) is the single backslash operator A\b — and it picks a good algorithm for you (it does not naïvely compute an inverse).

linalg.m Octave
A = [2 1; 1 3];
b = [3; 5];

x = A \ b          % solve A x = b → [0.8; 1.4]

det(A)               % 5
inv(A)               % the inverse (rarely what you need)
rank(A)              % 2
trace(A)             % 5

% eigenvalues and eigenvectors
[V, D] = eig(A);   % columns of V are eigenvectors,
                          % diagonal of D are eigenvalues

% least squares — same backslash, overdetermined system
M = [1 1; 1 2; 1 3];
y = [1; 2; 2];
coeffs = M \ y     % best-fit line via least squares
Why backslash, not inv Computing inv(A)*b is slower and less accurate than A\b. The backslash factorises \(A\) (LU for square, QR for tall) and solves directly. Reach for inv only when you genuinely need the inverse matrix itself.
14 — GNU Octave

Numerical methods

Three classics you'll meet constantly: finding a root of \(f\), integrating \(f\) over an interval, and fitting a polynomial. Each is built in, but knowing the recipe lets you trust the answer — and the proofs below show why these recipes converge.

numerics.m Octave
% --- Newton's method by hand: solve x^2 - 2 = 0 (find sqrt 2) ---
f  = @(x) x.^2 - 2;
df = @(x) 2*x;
x  = 1;                       % initial guess
for k = 1:6
  x = x - f(x) / df(x);    % the Newton step
end
x                              % 1.41421356237  (≈ sqrt 2)

% --- numerical integration of sin from 0 to pi (true value = 2) ---
g = @(x) sin(x);
I = quad(g, 0, pi)          % 2.0000   (adaptive quadrature)

% trapezoidal rule by hand on sampled data
xx = linspace(0, pi, 1000);
It = trapz(xx, sin(xx))    % ≈ 2.0000

% --- polynomial fit: degree-2 through noisy points ---
px = [0 1 2 3 4];
py = [1 3 7 13 21];
p = polyfit(px, py, 2)     % coefficients of best-fit parabola
polyval(p, 5)               % predict at x = 5
15 — GNU Octave · Interactive

Live sandboxes

These four widgets re-implement Octave's behaviour in JavaScript so they run in your browser right now. The algorithms are exactly those above — only the engine differs.

Matrix workbench

A op B

Type matrices in Octave syntax — spaces between columns, ; between rows. Try [1 2; 3 4].

ans =

Newton's method root-finder

f(x)=0

Enter f(x) using JS-style math (x*x-2, cos(x)-x, exp(x)-3). The derivative is taken numerically; watch the error square each step.

Numerical integrator

∫ trapezoid · Simpson

Approximate \(\int_a^b f(x)\,dx\) with the trapezoidal and Simpson rules and compare them.

∫ =

Function plotter

plot(x, f(x))

Octave's plot(x, y), in miniature. Enter a function and a domain.

16 — Proof

The sum the loops computed

Both the AppleScript counted loop and the Octave for loop summed \(1\) through \(100\) and got 5050. That is no accident — there is a closed form, and we prove it by mathematical induction.

Theorem 1Gauss's summation formula

For every integer \(n \ge 1\), \(\displaystyle\sum_{k=1}^{n} k = \frac{n(n+1)}{2}.\)

Proof (induction on \(n\)). Let \(P(n)\) be the statement \(\sum_{k=1}^{n} k = \frac{n(n+1)}{2}\).

Base case. For \(n=1\): the left side is \(1\), and the right side is \(\frac{1\cdot 2}{2}=1\). So \(P(1)\) holds.

Inductive step. Assume \(P(n)\) holds for some \(n\ge 1\); this is the inductive hypothesis. We show \(P(n+1)\):

$$\sum_{k=1}^{n+1} k \;=\; \left(\sum_{k=1}^{n} k\right) + (n+1) \;\overset{\text{IH}}{=}\; \frac{n(n+1)}{2} + (n+1).$$

Factor out \((n+1)\):

$$\frac{n(n+1)}{2} + (n+1) = (n+1)\!\left(\frac{n}{2}+1\right) = (n+1)\cdot\frac{n+2}{2} = \frac{(n+1)\big((n+1)+1\big)}{2}.$$

This is exactly \(P(n+1)\). By the principle of induction, \(P(n)\) holds for all \(n\ge 1\). In particular \(\sum_{k=1}^{100} k = \frac{100\cdot 101}{2} = 5050.\)

17 — Proof

Why A*(B*C) equals (A*B)*C

Octave's matrix product is associative — you can drop the parentheses. That convenience is a theorem about sums, and the proof is a clean exercise in swapping the order of summation.

Theorem 2Associativity of matrix multiplication

Let \(A\in\mathbb{R}^{m\times n}\), \(B\in\mathbb{R}^{n\times p}\), \(C\in\mathbb{R}^{p\times q}\). Then \((AB)C = A(BC)\).

Proof. Two matrices are equal iff every entry agrees, so we compute the \((i,\ell)\) entry of each side. Recall the definition of the product: \((XY)_{i\ell} = \sum_{r} X_{ir}Y_{r\ell}.\)

Start with the left side. Writing \(D = AB\) so that \(D_{ik} = \sum_{j=1}^{n} A_{ij}B_{jk}\),

$$\big((AB)C\big)_{i\ell} = \sum_{k=1}^{p} D_{ik}\,C_{k\ell} = \sum_{k=1}^{p}\left(\sum_{j=1}^{n} A_{ij}B_{jk}\right) C_{k\ell} = \sum_{k=1}^{p}\sum_{j=1}^{n} A_{ij}B_{jk}C_{k\ell}.$$

Each term \(A_{ij}B_{jk}C_{k\ell}\) is an ordinary real number, and real addition and multiplication are associative and commutative, so the finite double sum may be reordered. Exchange the order of summation:

$$\sum_{k=1}^{p}\sum_{j=1}^{n} A_{ij}B_{jk}C_{k\ell} = \sum_{j=1}^{n}\sum_{k=1}^{p} A_{ij}B_{jk}C_{k\ell} = \sum_{j=1}^{n} A_{ij}\left(\sum_{k=1}^{p} B_{jk}C_{k\ell}\right).$$

The inner sum is exactly \((BC)_{j\ell}\). Therefore

$$\sum_{j=1}^{n} A_{ij}\,(BC)_{j\ell} = \big(A(BC)\big)_{i\ell}.$$

Since the \((i,\ell)\) entries agree for all \(i,\ell\), we conclude \((AB)C = A(BC)\).

Footnote Note we did not assume the matrices were square or that multiplication commutes — it does not. Only associativity was needed, and that came entirely from associativity of the real numbers underneath.
18 — Proof

Newton's method, quadratically

In the live solver you watched the error roughly square at each step — \(10^{-2}\) became \(10^{-4}\), then \(10^{-8}\). That doubling of correct digits is quadratic convergence, and here is why it happens.

Theorem 3Local quadratic convergence of Newton's method

Let \(f\) be twice continuously differentiable near a root \(r\) with \(f(r)=0\) and \(f'(r)\neq 0\). Then for a starting point sufficiently close to \(r\), the Newton iterates \(x_{k+1}=x_k-\dfrac{f(x_k)}{f'(x_k)}\) satisfy, with \(e_k := x_k - r\), $$|e_{k+1}| \le M\,|e_k|^2 \qquad\text{where}\qquad M = \frac{\max|f''|}{2\min|f'|}.$$

Proof. Expand \(f\) about \(x_k\) using Taylor's theorem with the Lagrange remainder. Since \(f(r)=0\), there is a point \(\xi_k\) between \(x_k\) and \(r\) such that

$$0 = f(r) = f(x_k) + f'(x_k)(r-x_k) + \tfrac{1}{2}f''(\xi_k)(r-x_k)^2.$$

Writing \(e_k = x_k - r\) (so \(r - x_k = -e_k\)) this reads

$$0 = f(x_k) - f'(x_k)\,e_k + \tfrac{1}{2}f''(\xi_k)\,e_k^2.$$

Divide through by \(f'(x_k)\) (nonzero near the root) and solve for the combination that appears in the Newton step:

$$\frac{f(x_k)}{f'(x_k)} - e_k = -\frac{f''(\xi_k)}{2 f'(x_k)}\,e_k^2.$$

Now examine the new error. By the definition of the iteration,

$$e_{k+1} = x_{k+1} - r = \Big(x_k - \frac{f(x_k)}{f'(x_k)}\Big) - r = e_k - \frac{f(x_k)}{f'(x_k)}.$$

This is precisely the negative of the left-hand side above, so

$$e_{k+1} = \frac{f''(\xi_k)}{2 f'(x_k)}\,e_k^2.$$

Take absolute values. On a small closed interval around \(r\) the continuous functions are bounded: \(|f''|\le \max|f''|\) and \(|f'|\ge \min|f'| > 0\). Hence

$$|e_{k+1}| \le \frac{\max|f''|}{2\,\min|f'|}\,|e_k|^2 = M\,|e_k|^2.$$

If the first guess is close enough that \(M|e_0| < 1\), then \(|e_k|\to 0\), and the exponent \(2\) on \(|e_k|\) is exactly what makes the number of correct digits roughly double each step.

19 — Proof

The trapezoid's error

The integrator reported that doubling the panel count cut the error by about four. That is the signature of an \(O(h^2)\) method. We prove the error bound on a single panel; summing over panels gives the global result.

Theorem 4Error of the trapezoidal rule (one panel)

Let \(f\in C^2[a,b]\) and \(h=b-a\). The trapezoidal estimate \(T=\frac{h}{2}\big(f(a)+f(b)\big)\) satisfies, for some \(\eta\in(a,b)\), $$\int_a^b f(x)\,dx - T = -\frac{h^3}{12}\,f''(\eta).$$

Proof (sketch with the key step in full). Define the error as a function of the panel half-context by letting, for \(t\in[0,h]\),

$$E(t) = \int_{a}^{a+t} f(x)\,dx \;-\; \frac{t}{2}\big(f(a)+f(a+t)\big).$$

Then \(E(0)=0\) and the quantity we want is \(E(h)\). Differentiate using the fundamental theorem of calculus and the product rule:

$$E'(t) = f(a+t) - \tfrac{1}{2}\big(f(a)+f(a+t)\big) - \tfrac{t}{2}f'(a+t) = \tfrac{1}{2}\big(f(a+t)-f(a)\big) - \tfrac{t}{2}f'(a+t).$$

Differentiate once more; the \(\tfrac12 f(a)\) term is constant and drops out:

$$E''(t) = \tfrac{1}{2}f'(a+t) - \tfrac{1}{2}f'(a+t) - \tfrac{t}{2}f''(a+t) = -\frac{t}{2}\,f''(a+t).$$

Now integrate back twice from \(0\) to \(h\), using \(E(0)=E'(0)=0\). Apply the weighted mean value theorem for integrals: because the weight \(-\tfrac{t}{2}\) does not change sign on \([0,h]\), there exists \(\eta\in(a,b)\) with

$$E(h) = \int_0^h\!\!\int_0^s \Big(-\frac{t}{2}f''(a+t)\Big)dt\,ds = f''(\eta)\int_0^h\!\!\int_0^s\Big(-\frac{t}{2}\Big)dt\,ds.$$

The remaining integral is elementary: \(\int_0^s -\tfrac{t}{2}\,dt = -\tfrac{s^2}{4}\), and \(\int_0^h -\tfrac{s^2}{4}\,ds = -\tfrac{h^3}{12}.\) Therefore

$$\int_a^b f(x)\,dx - T = E(h) = -\frac{h^3}{12}\,f''(\eta).$$

Splitting \([a,b]\) into \(n\) equal panels of width \(h=(b-a)/n\) and summing gives a total error of size \(-\frac{(b-a)h^2}{12}f''(\bar\eta)=O(h^2)\): halving \(h\) (doubling \(n\)) divides the error by \(4\), exactly as the sandbox showed.

Appendix

Side-by-side cheat sheet

AppleScript

  • comment — -- … / (* … *)
  • assign — set x to 5
  • concat — a & b
  • list (1-indexed) — {1,2,3}
  • record — {name:"A", age:9}
  • if — if c then … end if
  • loop — repeat with i from 1 to n
  • function — on f(x) … end f
  • app — tell application "Finder"
  • errors — try … on error … end try
  • shell — do shell script "…"

GNU Octave

  • comment — % … / # …
  • assign (silent) — x = 5;
  • matrix — [1 2; 3 4]
  • range — 0:2:10, linspace(a,b,n)
  • matrix mul — A*B · elementwise A.*B
  • transpose / solve — A' · A\b
  • if — if c … elseif … end
  • loop — for k = 1:n … end
  • anon fn — f = @(x) x.^2
  • function — function y=f(x) … endfunction
  • reduce — sum mean max cumsum