The Windows function returns all slices, or contiguous subarrays, with shape (well, shape prefix) π¨
from π©
. It might also be seen as sliding a moving window along π©
.
This function replaces APL's Windowed Reduction, J's more general Infix operator, and Dyalog APL's Stencil, which is adapted from one case of J's Cut operator. In BQN, it's strongly preferred to use functions, and not modifiers, for array manipulation. Functions are simpler with fewer moving parts, and more concrete, since the array results can always be viewed right away.
We'll start with the one-axis case. Here π¨
is a number between 0
and 1+β π©
. The result is composed of slices of π©
(contiguous sections of major cells) with length π¨
, starting at each possible index in order.
5β"abcdefg" ββ β΅"abcde bcdef cdefg" β
There are 1+(β π©)-π¨
, or (β π©)Β¬π¨
, of these sections, because the starting index must be at least 0
and at most (β π©)-π¨
. Another way to find this result is to look at the number of cells in or before a given slice: there are always π¨
in the slice and there are only β π©
in total, so the number of slices is the range spanned by these two endpoints.
A single slice of an array π©
with length l
and starting index i
is lβiβπ©
, using Take and Drop. The Prefixes function returns all the slices that end at the end of the array ((β π©)=i+l
), and Suffixes gives the slices that start at the beginning (i=0
). Windows gives yet another collection of slices: the ones that have a fixed length l=π¨
. Selecting one cell from its result gives the slice starting at that cell's index:
2β5β"abcdefg" "cdefg" 5β2β"abcdefg" "cdefg"
Windows differs from Prefixes and Suffixes in that it doesn't add a layer of nesting (it doesn't enclose each slice). This is possible because the slices have a fixed size, so they fit together as cells of an array.
Windows can be followed up with Insert on each slice to give a windowed reduction or fold. Here we take running sums of 3 values.
βοΈ+ΛΛ3β β¨2,6,0,1,4,3β© β¨ 8 7 5 8 β©
A common task is to act on windows with an initial or final element so the total length stays the same. When using windows of length 2, the best way to accomplish this is with a shift Β«
or Β»
. If the window length is longer or variable, then a trick with Windows works better: add the elements, and then use windows matching the original length. Here we invert Plus Scan +`
, which requires we take pairwise differences starting at initial value 0.
-β(0Β»β’) +` 3βΏ2βΏ1βΏ1 β¨ 3 2 1 1 β© (-ΛΛβ β0βΎβ’) +` 3βΏ2βΏ1βΏ1 β¨ 3 2 1 1 β©
With Windows, we can modify the 3-element running sum from before to keep the length constant by starting with two zeros.
βοΈ(+Λβ β(2β₯0)βΈβΎ) β¨2,6,0,1,4,3β© β¨ 2 8 8 7 5 8 β©
Let's look at the first example, paired with its Transpose (β
).
βββ 5β"abcdefg" ββ Β· ββ ββ β΅"abcde β΅"abc bcdef bcd cdefg" cde β def efg" β β
Although the two arrays have different shapes, they're identical in the 3Γ3 region where they overlap.
βοΈβ‘β(3βΏ3βΈβ)ββ 5β"abcdefg" 1
More concretely, the i
th element of slice j
is the same as the j
th element of slice i
: it's the i+j
th element of the argument. So transposing still gives a possible result of Windows, but with a different slice length. The two lengths are related by Span, which converts between length and number of slices.
{(5βπ©)β‘β(3βπ©)}"abcdefg" 1 (β "abcdefg") Β¬ 3 5
The right argument can have rank more than 1, and it's viewed as a list of major cells following leading axis principles. As an example, Windows can take two-row slices of a shape 3βΏ4
array.
2β["0123","abcd","ABCD"] ββ β"0123 abcd Β·abcd ABCD" β <β2 2β["0123","abcd","ABCD"] ββ Β· ββ ββ β΅"0123 β΅"abcd abcd" ABCD" β β β
In the second version we've enclosed each slice with <β2
for viewingβa slice has rank 2, the same as π©
. Passing a list as the left argument to Windows takes slices along any number of leading axes. Here are all the shape 2βΏ2
slices:
<β2 2βΏ2β["0123","abcd","ABCD"] ββ β΅ ββ ββ ββ β΅"01 β΅"12 β΅"23 ab" bc" cd" β β β ββ ββ ββ β΅"ab β΅"bc β΅"cd AB" BC" CD" β β β β
The slices are naturally arranged along multiple dimensions according to their starting index. Once again the equivalence iβlβx
ββ lβiβx
holds, provided i
and l
have the same length.
If π¨
has length 0
, then π©
is not sliced along any dimensions. The only slice that resultsβthe entire argumentβis then arranged along an additional zero dimensions. In the end, the result is π©
, unchanged.
Here's a more formal definition: π©
is an array. π¨
is a number, or numeric list or unit, with length lββ π¨
so that lβ€=π©
. The result z
has shape π¨ βΎ Β¬βπ¨βΎ(lβΈβ)β’π©
, and element iβz
is jβπ©
, with jβ+´¨(lβΎββ=π©)βi
. That is, the index list i
starts with two length-l
sequences that are added together to produce the first l
values in j
. We might also say that each of the first l
values in j
is split into two values in i
.