Chapter 4 of 8

Adverbs: each, over, scan and prior

An adverb takes a verb and returns a new verb that applies it differently. Six of them cover nearly everything, and they compose with every verb you know, which is why the vocabulary stays small.

Over ( / ) — collapse a list to a value

+/ 1 2 3 4             / 10
*/ 1 2 3 4             / 24
|/ 1 2 3 4             / 4
&/ 1 2 3 4             / 1
{x+y*y}/0 1 2 3        / 14   -- any dyadic function folds

With a left argument, that value seeds the fold:

100 +/ 1 2 3           / 106

Scan ( \ ) — the running version of over

+\ 1 2 3 4             / 1 3 6 10     -- running sum
|\ 3 1 4 1 5           / 3 3 4 4 5    -- running maximum
&\ 3 1 4 1 5           / 3 1 1 1 1    -- running minimum
*\ 1 2 3 4             / 1 2 6 24
Running maximum is a drawdown in one character

|\px is the high-water mark of a price series, so px - |\px is the drawdown at every point, and &/px - |\px is the maximum drawdown. No loop, no state, one pass.

px: 100 104 101 108 99 103
|\ px                  / 100 104 104 108 108 108
px - |\ px             / 0 0 -3 0 -9 -5
&/ px - |\ px          / -9      -- max drawdown

Each ( ' ) — apply item by item

Most verbs are already elementwise, so ' is for when the items are themselves lists:

#'("ab";"cde";"")       / 2 3 0         -- length of each item
|'(1 2;3 4 5)          / (2 1;5 4 3)  -- reverse each
(+/)'(1 2;3 4 5)       / 3 12         -- sum each
An adverb binds to the verb on its left, with no space

#'x works; # ' x does not. The space separates the adverb from the verb it is meant to modify, and you get a wrong answer rather than an error. When the verb is itself derived, parenthesise it: (+/)'x.

Each-prior ( ': ) — compare neighbours

-':1 3 6 10            / 1 2 3 4              -- successive differences
deltas 1 3 6 10        / 1 2 3 4              -- the library name for it
,':1 3 6 10            / (,1;3 1;6 3;10 6)    -- the pairs it actually forms
1_{x-y}':1 3 6 10      / 2 3 4                -- a lambda: x current, y prior
A lambda does not get the primitive's seed

,': shows the pairing: element i is f[x_i; x_i-1], so current first, prior second. -': knows its own identity and seeds the first element with 0, which is why it gives 1 2 3 4. A user lambda does not: its first element is seeded with 0W, so {x-y}':1 3 6 10 starts with a huge negative number. Drop it with 1_, or seed it yourself with a left argument: 1{x-y}':1 3 6 10.

This is the natural way to express "how much did it move" over a series, and it pairs with scan: +\ and -': are inverses.

Each-right ( /: ) and each-left ( \: )

These fix one argument and sweep the other, giving the outer-product idiom:

1 2 3+/:10 20          / (11 12 13;21 22 23)  -- each-RIGHT: one row per right item
1 2 3+\:10 20          / (11 21;12 22;13 23)  -- each-LEFT:  one row per left item
(!3)=\:!3              / (1 0 0;0 1 0;0 0 1)  -- identity matrix

Composing them

The point of adverbs is that they stack. A weighted average is a fold over a product; a correlation is three folds; a moving sum is a scan and a difference:

w: 1 2 3 4
v: 10 20 30 40
(+/ w * v) % +/ w      / 30.0    -- weighted mean, written out
wavg[w; v]             / 30.0    -- ... and the library verb for it
Write the idiom, get the kernel

+/x*y is recognised by the compiler and compiled to a single fused dot-product primitive, so no intermediate product vector is ever written. The same is true of +/x<y, x@&m and +/(a+s*b)@&m. Chapter 8 lists them.