This document describes the actions and transformations available for PAD sequences.
An action runs over a sequence once it has been generated. There are 29 of them, listed in
the summary table at the end. Three other things are written the same way and are easy to
mistake for actions: plays (add=5, keep, remove) apply a sequence type to each
value as it is generated, and options (rows, from, to, increment) shape the
generation itself. They are covered in SEQUENCES.md and
PAD.md.
{pull:nums reverse} # Reverse order
{pull:nums sort} # Sort ascending
{pull:nums shuffle} # Randomize order
{pull:nums randomize} # Pick at random - see below, not the same as shuffle
shuffle reorders every value. randomize picks: bare it draws as many as there are, so
it looks like a shuffle, but randomize=3 draws three - and more than there are draws them
all, each once. Three options change how it draws:
orderly - the drawn values keep the order they had, instead of coming out shuffledduplicates - a value may be drawn more than once, so randomize=9 of five values draws nineatLeastOnce - every value is drawn at least once before any is drawn twice, which
implies duplicates. Asking for fewer than there are cannot satisfy that, and the count
gives way: randomize=2, atLeastOnce over five values returns all fivesort, dedup and reverse take an optional store name, and then work on the union of the
two: {pull:a sort='b'} sorts a and b together rather than sorting a by a field named b.
{pull:nums first} # First element
{pull:nums first=3} # First 3 elements
{pull:nums last=3} # Last 3 elements
{pull:nums element=5} # Get 5th element, counting from 1
{pull:nums slice='3|4'} # From offset 3, length 4 - counting from 0
{pull:nums minimum} # Smallest value
{pull:nums minimum=3} # The 3 smallest, back in their original order
{pull:nums maximum=3} # The 3 largest
{pull:nums shift=2} # See "Destructive selection" below
{pull:nums pop=2} # See "Destructive selection" below
element counts from 1, as from and to do. slice and splice count from 0, because
they are PHP’s array functions underneath.
left=N and right=N are not selectors on their own - they do nothing unless trim is
also given, where they say how much to take off each end. See Trim Operations.
A position the sequence does not reach - element=10 of three values - gives nothing rather
than an error.
shift and pop take values out of the stored sequence. What the tag shows is the part
removed, and what stays in the store is the remainder:
{sequence '1..10', push='nums'}
{pull:nums shift=2}{$sequence} {/pull:nums} # shows 1 2
{pull:nums}{$sequence} {/pull:nums} # the store now holds 3 4 5 6 7 8 9 10
A pull written as a single tag stores what it made back under the store’s name, whatever
the action - {pull:nums first=2/} leaves nums holding 1 2, {pull:nums reverse/} leaves
it reversed - as {resume} does. Written as a pair, {pull:nums first=2}...{/pull:nums}, it
leaves the store as it was, and only shift and pop take values out. push='x', name='x'
and toData='x' on a shift or pop keep what the tag shows, the part removed.
{pull:nums first=5, negative} # All EXCEPT first 5
{pull:nums last=5, negative} # All EXCEPT last 5
negative inverts what an action picked, and is applied after each action in turn. It
does nothing to a play: keep='prime', negative is not the complement of keep='prime' -
remove='prime' is.
trim removes values from the ends. both, left and right say how many, and mean
nothing without it.
{pull:nums trim, both=5} # Trim 5 from each end
{pull:nums trim, left=5} # Trim 5 from left
{pull:nums trim, right=5} # Trim 5 from right
{pull:nums trim, left=10, right=5} # Different amounts each side
{pull:nums trim=2} # 2 from each end - a value on trim itself fills both
A count on trim itself fills both, and left and right are then taken off on top of
it: trim=2, left=3 removes five from the left and two from the right. A count of 0 removes
nothing.
Apply an expression to each element:
{pull:nums eval='* 10 | - 1'} # Multiply by 10, subtract 1
{pull:nums eval='15 + @'} # Add 15 to each (@ = current value)
{pull:nums sum} # Sum of all elements
{pull:nums product} # Product of all elements
{pull:nums average} # Mean value
{pull:nums median} # Median: values sorted, middle one, or the mean of the two middles
{pull:nums count} # Number of elements
{pull:nums distinct} # Count of unique values
{pull:nums dedup} # Remove duplicates
Over an empty sequence, count, sum, distinct and product answer 0, 0, 0 and 1, while
average, median and element have no answer to give and leave it empty.
sum, product, average and median work on the numbers among the values - a word a list
or a store holds is left out, and values with no number among them have no mean or median.
unique is not an action. It is a generation option that drops a repeated value as the
sequence is built, so it runs before any action does.
These are sequence types applied to each value as it is generated, so they run before the
actions do. A parameter that is not a number is reported; divide will not take a zero.
{pull:nums add=5} # Add 5 to each element
{pull:nums subtract=3} # Subtract 3 from each
{pull:nums multiply=2} # Multiply each by 2
{pull:nums divide=4} # Divide each by 4
{pull:nums modulo=3} # Modulo 3 of each
{pull:nums power=2} # 2 raised to each element
{pull:nums exponentiation=2} # Square each element
{pull:nums exponentiation=3} # Cube each element
{pull:nums ceil} # Ceiling of each
{pull:nums floor} # Floor of each
{pull:nums round} # Round each
{pull:nums negation} # Negate each
increment is not an action either - it is the option that sets the step a sequence counts
in; after a pull it steps through the stored list: {pull:nums increment=3} over 1..10 gives
1 4 7 10.
These work on the bits of each value, not on true and false:
{pull:nums and=12} # Bitwise AND with 12
{pull:nums or=5} # Bitwise OR
{pull:nums not} # Bitwise NOT
{pull:nums nand=12} # Bitwise NAND - runs negative
{pull:nums nor=5} # Bitwise NOR - runs negative
{pull:nums xor=3} # Bitwise XOR
{pull:nums xnor=3} # Bitwise XNOR - runs negative
With seqA holding 1 to 6 and seqB holding 4 to 9:
{sequence '1..6', push='seqA'}
{sequence '4..9', push='seqB'}
{pull:seqA append='seqB'} # 1 2 3 4 5 6 4 5 6 7 8 9 - seqB on the end
{pull:seqA prepend='seqB'} # 4 5 6 7 8 9 1 2 3 4 5 6 - seqB in front
{pull:seqA combine='seqB'} # 1 2 3 4 4 5 5 6 6 7 8 9 - in order, duplicates kept
{pull:seqA merge='seqB'} # 1 2 3 4 5 6 7 8 9 - in order, duplicates dropped
{pull:seqA intersection='seqB'} # 4 5 6 - in both
{pull:seqA difference='seqB'} # 1 2 3 7 8 9 - in one but not both
{pull:seqA onlyNow='seqB'} # 1 2 3 - in seqA only
{pull:seqA onlyStore='seqB'} # 7 8 9 - in seqB only
combine and merge put both in value order first, whatever order they are in: with seqA
holding 3 1 and seqB 2 0, combine='seqB' gives 0 1 2 3.
difference is the symmetric difference: the values only in the sequence, followed by the
values only in the store. For the one-sided operation use onlyNow.
Every one of these needs a store to work with. Named without one, or named with a store that
was never pushed, they leave the sequence as it is - except intersection, which under the
strict check names the store that is not there.
resume TagApplies transformations to a stored sequence without pulling it:
{sequence 25, push='mySeq'}
{resume add=100} # Add 100 to each value
{resume reverse} # Reverse order
{resume subtract=5} # Subtract 5 from each
{pull:mySeq}{$sequence} {/pull:mySeq}
It resumes the store pushed last, or the one it names - {resume 'mySeq', reverse},
{resume mySeq, reverse} or pull='mySeq'. A name that is no store is reported, as
{pull:typo} reports it, and no other store is touched. A quoted action name is that action:
{resume 'reverse'} is {resume reverse}. The parameters after it are read as on the other
sequence tags (SEQUENCES.md): {resume 'first', 2} and
{resume first, 2} are {resume first=2}, {resume 'mySeq', 'reverse'} is
{resume mySeq, reverse}, and one nothing reads is reported. push='u' and name='u' keep
the result under u as well, with the columns a later {pull:u} chains in, and after
push='u' u is the store pushed last - as the store resumed is after push='mySeq' on it, or
a bare push.
Filter or transform sequences based on sequence types:
{pull mySeq, keep, even} # Keep only even values
{pull mySeq, remove, odd} # Remove odd values
{pull mySeq, make, prime} # Transform using prime
{pull mySeq, flag, even} # Mark even entries
{sequence '1..10', push='nums'}
{resume add=5}
{resume reverse}
{pull:nums sort}{$sequence} {/pull:nums}
{sequence '1..5', push='seqA'}
{sequence '10..15', push='seqB'}
{pull:seqA append='seqB'}{$sequence} {/pull:seqA}
{sequence '1..100', push='nums'}
Sum: {pull:nums sum}{$sequence}{/pull:nums}
Avg: {pull:nums average}{$sequence}{/pull:nums}
The 29 actions, and nothing else - from, to, rows, increment and unique are
generation options, and the arithmetic and bitwise entries above are plays.
| Action | Description |
|---|---|
reverse |
Reverse order. With a store name, reverses the union |
sort |
Sort ascending. With a store name, sorts the union |
shuffle |
Reorder at random |
randomize / randomize=N |
Draw N at random, all of them when bare |
first / first=N |
Keep the first N, 1 when bare |
last / last=N |
Keep the last N |
element=N |
The Nth value, counting from 1; nothing if there is no Nth |
slice='pos\|len' |
From offset pos, counting from 0, for len values |
splice='pos\|len' or 'pos\|len\|seq' |
Remove len values at pos, optionally putting a store in their place |
shift=N |
Take the first N out of the store and show them |
pop=N |
Take the last N out of the store and show them |
trim |
Remove from the ends, by both / left / right |
sum |
Sum of the values, 0 when empty |
product |
Product of the values, 1 when empty |
average |
Mean; nothing when empty |
median |
Values sorted, the middle one or the mean of the two middles |
minimum / minimum=N |
The smallest, or the N smallest in their original order |
maximum / maximum=N |
The largest, or the N largest |
count |
How many values, 0 when empty |
distinct |
How many different values |
dedup |
Remove duplicates. With a store name, dedups the union |
append='seq' |
Add a store’s values to the end |
prepend='seq' |
Add a store’s values to the front |
merge='seq' |
Merge in order, dropping duplicates |
combine='seq' |
Merge in order, keeping duplicates |
intersection='seq' |
Values in both |
difference='seq' |
Values in one or the other but not both |
onlyNow='seq' |
Values in the sequence but not the store |
onlyStore='seq' |
Values in the store but not the sequence |
negative is no action but an option of them: it inverts whatever the action just selected.
| Prefix | Purpose | Example |
|---|---|---|
pull: |
Stored sequence | {pull:mySeq} |
action: |
Sequence action | {action:reverse} |
An action named by the tag takes its first parameter as its own when it is the action’s kind
of parameter - the name of a store or a list for append, merge, sort and the others that
take stores, and for first, shift and the others that count a count (an offset for slice
and splice): anything but the name of a store or a type. Anything else is read as any first
parameter is. A store or a type it names is what the action runs over: {sum 'nums'},
{action:sum $name} and {action:first 'nums'} act on nums, as {pull:nums sum} does, and
{reverse 'prime'} reverses the primes, as {prime reverse} does. A number is how many rows:
{append 5} is {sequence 5, append}, as {sum 5} is {sequence 5, sum}. What none of it
reads - {sum 'abc'} or {reverse 'abc'} with no store abc - is reported by the strict check.