Control Flow in APL
Learn how APL handles control flow through array operations, conditional expressions, and the rare cases where loops are still useful
Control flow in APL looks very different from what you’ve seen in C-family or Python-style languages. Because APL is array-oriented, most decisions and iterations that other languages spell out with if, for, and while simply disappear — a single array expression handles every element at once. When you genuinely need branching, APL offers several distinct techniques: arithmetic conditionals, selection by boolean masks, the :If/:While control structures from GNU APL, and the classic → (goto) branch arrow.
This tutorial walks through how to think in arrays first, then shows the explicit conditional and looping constructs for the cases where array thinking isn’t a clean fit. The goal isn’t to translate for (i=0; i<n; i++) into APL — it’s to recognize when you don’t need that loop at all.
Conditionals as Arithmetic
APL’s comparison operators (<, ≤, =, ≥, >, ≠) return 1 for true and 0 for false. Because booleans are just numbers, you can use them directly in arithmetic — this is how APL replaces many if/else chains. The pattern (cond × a) + (~cond) × b selects a when cond is true and b when it is false, element by element.
Create a file named conditional_arith.apl:
| |
The expression (nums ≥ 0) × nums is APL’s version of max(0, n) applied to every element. There is no loop, no if, no temporary array — the boolean mask nums ≥ 0 multiplies element-wise against the values, and 0 annihilates the negatives.
Selection with Compress and Where
When you only want the elements that satisfy a condition — APL’s equivalent of filter — use the compress operator /. A boolean vector on the left of / selects the matching elements on the right.
Create a file named selection.apl:
| |
The phrase +/ passing reduces the boolean mask with addition — counting trues. This is the array-language idiom that replaces a for loop with an incrementing counter inside an if.
Explicit If/Else Expressions
When a decision really is scalar — picking a single message to display, choosing a branch based on user input — APL still gives you readable conditional expressions. GNU APL supports the :If / :ElseIf / :Else / :EndIf control structure inside defined functions, and you can also use the indexing trick (cond) ⊃ alternatives for an expression-level ternary.
Create a file named if_else.apl:
| |
Two complementary styles: the one-line classify uses indexing into a list of alternatives — APL’s natural ternary — and the multi-line grade uses the :If block when several branches need to read cleanly top-to-bottom.
While Loops and Iteration
Sometimes you genuinely need iteration that can’t be expressed as an array operation — anything that updates state until a condition is met, like Newton’s method or a search that may stop early. APL has :While and :Until for these cases, and also the older → branch arrow that jumps to a labeled line.
Create a file named loops.apl:
| |
The :While block runs as long as its condition is true; :Until runs at least once and stops when its condition becomes true. Note how newton_sqrt uses local variables (declared with ;guess;next after the function header) — these are private to the function call.
Loop-Free Iteration with Reduce and Scan
The most important “control flow” tool in APL isn’t a loop at all — it’s the reduce operator / and the scan operator \. Both apply a function between every adjacent pair of elements in an array, replacing whole categories of accumulating loops.
Create a file named reduce_scan.apl:
| |
+/, ×/, ⌈/, ⌊/, ∧/, and ∨/ together replace most for loops that compute a running total, a maximum, or a “does any element satisfy X” check. When you find yourself reaching for :While in APL, pause and ask whether reduce or scan can do it instead — usually they can.
Running with Docker
| |
Expected Output
Output from conditional_arith.apl:
Original:
3 ¯7 12 0 ¯4 8
Absolute:
3 7 12 0 4 8
Sign of each element:
1 ¯1 1 0 ¯1 1
Mask of "big" values:
0 0 1 0 0 1
Negatives clamped to zero:
3 0 12 0 0 8
Output from selection.apl:
Mask:
1 1 0 1 0 0 1 1
Passing scores:
72 88 91 99 80
Number passing:
5
Positions of passing scores:
1 2 4 7 8
Average passing score:
86
Output from if_else.apl:
classify 5 →
positive
classify ¯3 →
non-positive
classify 0 →
non-positive
grade 95 →
A
grade 82 →
B
grade 71 →
C
grade 40 →
F
Output from loops.apl:
sqrt of 2 ≈
1.414213562
sqrt of 16 =
4
sqrt of 50 ≈
7.071067812
Collatz 6:
6 3 10 5 16 8 4 2 1
Collatz 11:
11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
Output from reduce_scan.apl:
Sum (+/):
15
Product (×/):
120
Maximum (⌈/):
5
Minimum (⌊/):
1
Running sum (+\):
1 3 6 10 15
Running product (×\):
1 2 6 24 120
Running max (⌈\):
3 3 4 4 5 9 9 9
All positive?
1
Any zero?
0
Numbers 1..15:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Divisible by 3:
0 0 1 0 0 1 0 0 1 0 0 1 0 0 1
Divisible by 5:
0 0 0 0 1 0 0 0 0 1 0 0 0 0 1
Divisible by both:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
Key Concepts
- Booleans are numbers — comparison operators return
0or1, and you can multiply, add, and sum them like any other value. Most simpleif/elsepatterns collapse into arithmetic on boolean masks. - Compress (
/) replaces filter loops — a boolean vector on the left selects matching elements from the right. Combine with+/to count, or with reduction to compute summary statistics on the matches. - Reduce (
+/,×/,⌈/,∧/,∨/) replaces accumulator loops — any pattern of “start with X, walk the array, combine with each element” is a reduction. - Scan (
\) replaces running-total loops — when you need every intermediate value of a reduction, use scan instead. :If/:ElseIf/:Else/:EndIfis the explicit conditional block for defined functions;(cond) ⊃ alternativesis the inline ternary form.:Whileand:Untilexist for iterations that genuinely depend on a runtime stop condition (numerical methods, convergence, search). Reach for them last — array operations cover most cases.- Defined functions use
∇ … ∇and can declare local variables after the header with;name, keeping state private to a single call. - Think in shapes, not steps — before writing a loop in APL, ask whether the answer can be expressed as a transformation of a whole array. The answer is usually yes.
Running Today
All examples can be run using Docker:
docker pull juergensauermann/gnu-apl-1.8:latest
Comments
Loading comments...
Leave a Comment