The Each iterators
Functions derived by Each and its variants work independently on their argument items.
Apart from Case, the map iterators are variations of Each '.
Each
The Each iterator applied to a function f derives a function f' of the same rank as f.
Where f has rank \(N\), you can apply f' with bracket syntax to an argument list of \(N\) conforming lists or dictionaries.
q)/ rank 1
q)til'[2 0 5]
0 1
`long$()
0 1 2 3 4
f' iterates f over corresponding items from the list arguments.
q)/ rank 2
q)?'[("abc";"defg";"qrs";"tuvw");"best"]
1 1 2 0
("abc"?"b";"defg"?"e";"qrs"?"s";"tuvw"?"t").
q)/ rank 3
q){x~y rotate asc z}'[("abc";"fed");0 1;("cab";"def")]
10b
Unary functions
Where f is unary, so too is f'.
But you can’t apply a unary f' with prefix syntax.
Why not? Because…
All derived functions have infix syntax regardless of their rank.
Why is that? Don’t know. Brute fact. Mysteries of the q parser.
So you cannot apply unary f' with prefix syntax (e.g. e.g. count'x – wrong!) because the parser sees count' has infix syntax and looks for a left argument that count' does not and cannot have.
You can however parenthesise the derived function, which captures it as a noun (count'), and you can apply a noun with prefix syntax.
So (count')x works.
For the same reason you cannot make count' the left argument of Apply At @ with infix syntax (i.e. count'@x – wrong!), but you can use it as an item of its argument list.
q)@[count';("quick";"brown";"fox")]
5 5 3
each keyword (see below), and that is preferred style.
q)count each ("quick";"brown";"fox")
5 5 3
Summary:
f'[x] / works: bracket syntax
f'x / ERROR: prefix syntax
(f')x / works: (noun) & prefix syntax
f each x / preferred: keyword
Binary functions
Where f is binary, good q style applies it with infix syntax as x f'y.
Each-both
Because
- the
eachkeyword is good style for unary functions - most q primitives are unary or binary
f'has infix syntax, allowingx f'y
the Each iterator has sometimes been called each-both. This wrongly suggests it is limited to binary functions, which is not true.
Summary:
f'[x;y] / works: bracket syntax
x f'y / preferred: infix sytax
Higher-rank functions
Where f has rank 3–8, f' will iterate it through list arguments.
Use bracket syntax f'[x;y;z].
q)
q)ssr'[("cow";"sheep";"cat";"dog");("c";"ee";"c";"g");"sobe"]
"sow"
"shop"
"bat"
"doe"
Summary:
f'[x;y;z;..]
Each Left and Each Right
The Each Left \: and Each Right /: iterators are syntactic sugar for the common iterations of binary functions in which either x or y is the same for each iteration.
q)"abc",\:"def" / Each Left
"adef"
"bdef"
"cdef"
q)"abc",/:"def" / Each Right
"abcd"
"abce"
"abcf"
p:,["abc"] / projection
p'["def"] / bracket syntax
p each "def" / keyword
(p') "def" / (noun) prefix
Each Parallel
Where f is a unary (or variadic) function, Each Parallel f': iterates f as a unary through the items of a list or dictionary, dividing the work between secondary tasks.
Where no secondary tasks are configured, Each Parallel is the same as Each.
Multi-threading primitives
The multi-threading primitives feature introduced in kdb+ v4.0 makes Each Parallel redundant for q primitives.
Preferred style is to use the peach keyword.
Summary:
f':[x] / works: bracket syntax
f peach x / preferred: keyword
Each Prior
Where f is a binary function (or rank-2 list or dictionary)
f':[y] <==> y f' I , -1_y
x f':y <==> y f' enlist[x] , -1_y
f is one of these six operators:
f: + - * % & ~
I: 0 0 1 1 0W (::)
(apart from Match1, right-identity elements for f)
otherwise I is first y -1.
Each Prior and vectors
Apart from the six operators, for vector y
f':[y] <==> y f'prev y <==> (f) prior y
q)mod':[3 4 5]
0N 1 1
q)3 4 5 mod'prev 3 4 5
0N 1 1
q)(mod) prior 3 4 5
0N 1 1
q)prev"abc"
" ab"
q),':["abc"] / Join Each Prior
"a "
"ba"
"cb"
q)show A:4 5#.Q.a
"abcde"
"fghij"
"klmno"
"pqrst"
q)2 1 3 A'prev 2 1 3
" hq"
q)A':[2 1 3] / A Each Prior
" hq"
Non-vector lists
If the first item of a general list is an atom, the derived function can be applied, provided adjacent items conform.
q)q <' prev q:(3;4;99 100;01b;6;50 51 51;7;8)
0b
0b
00b
11b
00b
000b
111b
0b
q)<':[q] ~ q <' prev q
1b
y 0 is not an atom, prev y would return a leading empty list, but for an operator Each Prior substitutes I as above.
q)q < prev q:(99 100;3;4;5)
'length
[0] q < prev q:(99 100;3;4;5)
^
q)prev q
`long$()
99 100
3
4
q)first q -1
0N
q)<':[q] / first item is 99 100<0N
00b
11b
0b
0b
First items must have non-zero data types.
If 0=type first y, then I will be () and f':[y] will signal a length error.
Keywords
Four keywords correspond to commonly used functions derived by Each Prior.
deltas -':
differ <>':
prev -':
ratios %':
The keywords are variadic, but use them only as unaries.
These keywords are wrappers for the derived functions and so are also variadic.
q)a
-2 1 -3 4 -1 2 1 -5 4
q)deltas a / unary
-2 3 -4 7 -5 3 -1 -6 9
q)deltas[1000;a] / binary
-1002 3 -4 7 -5 3 -1 -6 9
q)deltas a / unary
-2 3 -4 7 -5 3 -1 -6 9
q)1000-':a / binary
-1002 3 -4 7 -5 3 -1 -6 9
Iterator keywords
Keywords each, peach, and prior are not iterators but binary keywords with infix syntax.
Their left arguments are functions.
You can ensure a function is parsed as the argument of a keyword (rather than evaluated) by parenthesising it to make a noun.
q)M:3 4#til 12
q)(+/) each M
6 22 38
(+/) has noun syntax and the derived function +/ is the left argument of each.
Parentheses are unnecessary where a function’s rank is unambiguous.
Above, +/ is variadic; and parentheses stop the parser looking for its arguments.
Below, count and sum are unambiguously unary, and need no parentheses.
q)sum each M
6 22 38
q)count each M
4 4 4
The keywords each and peach take unary left arguments; prior a binary.
q)(+)prior til 5
0 1 3 5 7
q){x+y} prior til 5
0N 1 3 5 7
Above, the lambda is unambiguously binary and is parsed as the left argument to prior.
But + has a (deprecated) unary form and needs parentheses around it.
Except for lambdas and unary keywords, parenthesise all left arguments of each, peach and prior.
So, count each x but (+) prior x.
Exercises
No peeking.
Attempt each exercise before looking at its answer.
The exercises clarify your thinking. Reading answers does not.
-
Simplify
3 4+'5 6.Answer
The Add operator has atomic iteration so the Each operator is redundant.
3 4+5 6 -
Simplify
("quick";"brown";"fox")?\:"o".Answer
Scalar extension makes the
"o"atom equivalent here to"ooo", so Each Left can be replaced with Each.("quick";"brown";"fox")?'"o" -
Explain why
count eachandcount'have different types.q)type each(count';count each) 106 104hAnswer
Functions derived by Each, such as
count'have type 106.In contrast,
count eachprojectseachontocountand is therefore equivalent toeach[count;]. As a projection, it has type 104. -
Simplify:
(,["https://";])each urlsAnswer
Above, Join
,is projected on"https://"and the projection forms the left argument ofeach. It can be simplified by omitting both the parentheses and the semicolon, but with no increase in clarity.Better: use Each Right.,["https://"] each urls"https://",/:urls -
Explain why the expressions below have different results.
q)(+) prior til 5 0 1 3 5 7 q){x+y} prior til 5 0N 1 3 5 7Answer
The lambda adds 0 (i.e.
til[5]@0) totil[5]@-1, which is the null0N.The operator is one of the list of special cases. It adds
til[5]@0to itself. -
Below, use
KEYto extractsecretfromcipher.q)secret:"mypassword" q)KEY:neg[1+n]?N:2*n:count secret q)show cipher:./[N cut(N*N)?.Q.a;1_,':[KEY];:; secret] "ipstdnynogqxjxwboipo" "rccwdhdgamjcsweyrudu" "rzfggdyjamjsomuypcqr" "awijrkjyzavfgkebpjlg" "pnrppyqrpveeyzjlexbx" "ailiexwmmrhlmfuijxzu" "khaqghfgelmfrilvibfm" "pntmethjkvfggvbpcyfw" "iunqbvillxfiqkoehrbw" "rxwwsesustqqrlznekcs" "ubfndhkcmhjuxlwnadax" "hkzhrgxvujbkodvgtlso" "rwhtrzhvlwfxrfyxfdue" "tpuhqfyiquhpzgysrlcz" "nbodbwrtpdnmimuubhdt" "bofzemgnzqtmkbcptgbs" "dgmvgkkjzajugcleamao" "pcwtdgepaoggdusfycuh" "kmxaeockvwyqtdcphlnm" "xvwsqmuokcapqhtlpghn"Answer
List
cipheris a matrix randomly filled with letters into whichsecretis inserted at indexes generated fromKEY.To extract
secretfromcipherregenerate the indices fromKEYwith Index Each Right./:q)cipher ./: 1_,':[KEY] "mypassword" -
Lesser (
&) is a special case when Each Prior is applied to it. Should Greater (|) also be a special case? Justify your answer.Answer
Because
1b ~ 0|0Nthe special-case model and the definitionx|'prev xof Greater Each Prior both return the same result. So, either- Yes
- Greater should join Lesser and other comparison operators in the list of special cases.
- No
-
Greater Each Prior conforms to the definition pattern and is not a special case.
|':[x] <==> x | prev x
-
Let
M:4 5#20?100. Amendsd:{sum x-y}so thatsd prior Mreturns a result.Answer
sd prior Msignals a length error withsd:{sum x-y}becausefirst prev Mis`long$().sd:{sum x-$[count y;y;0]}allowssd prior Mto evaluate.
-
This special treatment for Match ensures that the first item of the result of
differ xis1b– unlessx[0]is the general null! ↩