aboutsummaryrefslogtreecommitdiff
path: root/docs/doc/windows.html
blob: 0f85eab7f1605440c74eff73c3664debbb06cbfe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
<head>
  <link href="../favicon.ico" rel="shortcut icon" type="image/x-icon"/>
  <link href="../style.css" rel="stylesheet"/>
  <title>BQN: Windows</title>
</head>
<div class="nav"><a href="https://github.com/mlochbaum/BQN">BQN</a></div>
<h1 id="windows">Windows</h1>
<p>In BQN, it's strongly preferred to use functions, and not modifiers, for array manipulation. Functions are simpler as they have fewer moving parts. They are more concrete, since the array results can always be viewed right away. They are easier to implement with reasonable performance as well, since there is no need to recognize many possible function operands as special cases.</p>
<p>The Window function replaces APL's Windowed Reduction, J's more general Infix operator, and Dyalog's Stencil, which is adapted from one case of J's Cut operator.</p>
<h2 id="definition">Definition</h2>
<p>We'll start with the one-axis case. Here Window's left argument is a number between <code><span class='Number'>0</span></code> and <code><span class='Number'>1</span><span class='Function'>+β‰ </span><span class='Value'>𝕩</span></code>. The result is composed of slices of <code><span class='Value'>𝕩</span></code> (contiguous sections of major cells) with length <code><span class='Value'>𝕨</span></code>, starting at each possible index in order.</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=NeKGlSJhYmNkZWZnIg==&run">↗️</a><pre>    <span class='Number'>5</span><span class='Function'>↕</span><span class='String'>&quot;abcdefg&quot;</span>
β”Œβ”€       
β•΅"abcde  
  bcdef  
  cdefg" 
        β”˜
</pre>
<p>There are <code><span class='Number'>1</span><span class='Function'>+</span><span class='Paren'>(</span><span class='Function'>β‰ </span><span class='Value'>𝕩</span><span class='Paren'>)</span><span class='Function'>-</span><span class='Value'>𝕨</span></code>, or <code><span class='Paren'>(</span><span class='Function'>β‰ </span><span class='Value'>𝕩</span><span class='Paren'>)</span><span class='Function'>Β¬</span><span class='Value'>𝕨</span></code>, of these sections, because the starting index must be at least <code><span class='Number'>0</span></code> and at most <code><span class='Paren'>(</span><span class='Function'>β‰ </span><span class='Value'>𝕩</span><span class='Paren'>)</span><span class='Function'>-</span><span class='Value'>𝕨</span></code>. Another way to find this result is to look at the number of cells in or before a given slice: there are always <code><span class='Value'>𝕨</span></code> in the slice and there are only <code><span class='Function'>β‰ </span><span class='Value'>𝕩</span></code> in total, so the number of slices is the range spanned by these two endpoints.</p>
<p>You can take a slice of an array <code><span class='Value'>𝕩</span></code> that has length <code><span class='Value'>l</span></code> and starts at index <code><span class='Value'>i</span></code> using <code><span class='Value'>l</span><span class='Function'>↑</span><span class='Value'>i</span><span class='Function'>↓</span><span class='Value'>𝕩</span></code> or <code><span class='Value'>l</span><span class='Function'>↑</span><span class='Value'>i</span><span class='Function'>⌽</span><span class='Value'>𝕩</span></code>. The <a href="prefixes.html">Prefixes</a> function returns all the slices that end at the end of the array (<code><span class='Paren'>(</span><span class='Function'>β‰ </span><span class='Value'>𝕩</span><span class='Paren'>)</span><span class='Function'>=</span><span class='Value'>i</span><span class='Function'>+</span><span class='Value'>l</span></code>), and Suffixes gives the slices that start at the beginning (<code><span class='Value'>i</span><span class='Function'>=</span><span class='Number'>0</span></code>). Windows gives yet another collection of slices: the ones that have a fixed length <code><span class='Value'>l</span><span class='Function'>=</span><span class='Value'>𝕨</span></code>. Selecting one cell from its result gives you the slice starting at that cell's index:</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=MuKKjzXihpUiYWJjZGVmZyIKNeKGkTLihpMiYWJjZGVmZyI=&run">↗️</a><pre>    <span class='Number'>2</span><span class='Function'>⊏</span><span class='Number'>5</span><span class='Function'>↕</span><span class='String'>&quot;abcdefg&quot;</span>
"cdefg"
    <span class='Number'>5</span><span class='Function'>↑</span><span class='Number'>2</span><span class='Function'>↓</span><span class='String'>&quot;abcdefg&quot;</span>
"cdefg"
</pre>
<p>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.</p>
<h3 id="multiple-dimensions">Multiple dimensions</h3>
<p>The above description applies to a higher-rank right argument. As an example, we'll look at two-row slices of a shape <code><span class='Number'>3</span><span class='Ligature'>β€Ώ</span><span class='Number'>4</span></code> array. For convenience, we will enclose each slice. Note that slices always have the same rank as the argument array.</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=POKOiTIgMuKGlSIwMTIzIuKIviJhYmNkIuKJjSJBQkNEIg==&run">↗️</a><pre>    <span class='Function'>&lt;</span><span class='Modifier2'>βŽ‰</span><span class='Number'>2</span> <span class='Number'>2</span><span class='Function'>↕</span><span class='String'>&quot;0123&quot;</span><span class='Function'>∾</span><span class='String'>&quot;abcd&quot;</span><span class='Function'>≍</span><span class='String'>&quot;ABCD&quot;</span>
β”Œβ”€                   
Β· β”Œβ”€       β”Œβ”€        
  β•΅"0123   β•΅"abcd    
    abcd"    ABCD"   
         β”˜        β”˜  
                    β”˜
</pre>
<p>Passing a list as the left argument to Windows takes slices along any number of leading axes. Here are all the shape <code><span class='Number'>2</span><span class='Ligature'>β€Ώ</span><span class='Number'>2</span></code> slices:</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=POKOiTIgMuKAvzLihpUiMDEyMyLiiL4iYWJjZCLiiY0iQUJDRCI=&run">↗️</a><pre>    <span class='Function'>&lt;</span><span class='Modifier2'>βŽ‰</span><span class='Number'>2</span> <span class='Number'>2</span><span class='Ligature'>β€Ώ</span><span class='Number'>2</span><span class='Function'>↕</span><span class='String'>&quot;0123&quot;</span><span class='Function'>∾</span><span class='String'>&quot;abcd&quot;</span><span class='Function'>≍</span><span class='String'>&quot;ABCD&quot;</span>
β”Œβ”€                      
β•΅ β”Œβ”€     β”Œβ”€     β”Œβ”€      
  β•΅"01   β•΅"12   β•΅"23    
    ab"    bc"    cd"   
       β”˜      β”˜      β”˜  
  β”Œβ”€     β”Œβ”€     β”Œβ”€      
  β•΅"ab   β•΅"bc   β•΅"cd    
    AB"    BC"    CD"   
       β”˜      β”˜      β”˜  
                       β”˜
</pre>
<p>The slices are naturally arranged along multiple dimensions according to their starting index. Once again the equivalence <code><span class='Value'>i</span><span class='Function'>⊏</span><span class='Value'>l</span><span class='Function'>↕</span><span class='Value'>x</span></code> ←→ <code><span class='Value'>l</span><span class='Function'>↑</span><span class='Value'>i</span><span class='Function'>↓</span><span class='Value'>x</span></code> holds, provided <code><span class='Value'>i</span></code> and <code><span class='Value'>l</span></code> have the same length.</p>
<p>If the left argument has length <code><span class='Number'>0</span></code>, then the argument 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 the same as the argument.</p>
<h3 id="more-formally">More formally</h3>
<p><code><span class='Value'>𝕩</span></code> is an array. <code><span class='Value'>𝕨</span></code> is a number, or numeric list or unit, with <code><span class='Value'>𝕨</span><span class='Function'>≀</span><span class='Modifier2'>β—‹</span><span class='Function'>β‰ β‰’</span><span class='Value'>𝕩</span></code>. The result <code><span class='Value'>z</span></code> has shape <code><span class='Value'>𝕨</span><span class='Function'>∾¬</span><span class='Modifier2'>⟜</span><span class='Value'>𝕨</span><span class='Modifier2'>⌾</span><span class='Paren'>((</span><span class='Function'>β‰ </span><span class='Value'>𝕨</span><span class='Paren'>)</span><span class='Modifier2'>⊸</span><span class='Function'>↑</span><span class='Paren'>)</span><span class='Function'>β‰’</span><span class='Value'>𝕩</span></code>, and element <code><span class='Value'>i</span><span class='Function'>βŠ‘</span><span class='Value'>z</span></code> is <code><span class='Value'>𝕩</span><span class='Function'>βŠ‘</span><span class='Modifier'>˜</span><span class='Paren'>(</span><span class='Function'>β‰ </span><span class='Value'>𝕨</span><span class='Paren'>)(</span><span class='Function'>↑+</span><span class='Modifier2'>⌾</span><span class='Paren'>((</span><span class='Function'>β‰ </span><span class='Value'>𝕨</span><span class='Paren'>)</span><span class='Modifier2'>⊸</span><span class='Function'>↑</span><span class='Paren'>)</span><span class='Function'>↓</span><span class='Paren'>)</span><span class='Value'>i</span></code>.</p>
<p>Using <a href="group.html">Group</a> we could also write <code><span class='Value'>i</span><span class='Function'>βŠ‘</span><span class='Value'>z</span></code> ←→ <code><span class='Value'>𝕩</span><span class='Function'>βŠ‘</span><span class='Modifier'>˜</span><span class='Paren'>(</span><span class='Value'>𝕨</span><span class='Function'>∾</span><span class='Modifier2'>β—‹</span><span class='Paren'>(</span><span class='Function'>↕</span><span class='Modifier2'>∘</span><span class='Function'>β‰ </span><span class='Paren'>)</span><span class='Function'>β‰’</span><span class='Value'>𝕩</span><span class='Paren'>)</span> <span class='Function'>+</span><span class='Modifier'>´¨</span><span class='Modifier2'>∘</span><span class='Function'>βŠ”</span> <span class='Value'>i</span></code>.</p>
<h2 id="symmetry">Symmetry</h2>
<p>Let's look at an earlier example, along with its transpose.</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=e+KfqPCdlaks4o2J8J2VqeKfqX014oaVImFiY2RlZmci&run">↗️</a><pre>    <span class='Brace'>{</span><span class='Bracket'>⟨</span><span class='Value'>𝕩</span><span class='Separator'>,</span><span class='Function'>⍉</span><span class='Value'>𝕩</span><span class='Bracket'>⟩</span><span class='Brace'>}</span><span class='Number'>5</span><span class='Function'>↕</span><span class='String'>&quot;abcdefg&quot;</span>
β”Œβ”€                   
Β· β”Œβ”€        β”Œβ”€       
  β•΅"abcde   β•΅"abc    
    bcdef     bcd    
    cdefg"    cde    
          β”˜   def    
              efg"   
                  β”˜  
                    β”˜
</pre>
<p>Although the two arrays have different shapes, they are identical where they overlap.</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4omh4peLKDPigL8z4oq44oaRKeKfnOKNiTXihpUiYWJjZGVmZyI=&run">↗️</a><pre>    <span class='Function'>≑</span><span class='Modifier2'>β—‹</span><span class='Paren'>(</span><span class='Number'>3</span><span class='Ligature'>β€Ώ</span><span class='Number'>3</span><span class='Modifier2'>⊸</span><span class='Function'>↑</span><span class='Paren'>)</span><span class='Modifier2'>⟜</span><span class='Function'>⍉</span><span class='Number'>5</span><span class='Function'>↕</span><span class='String'>&quot;abcdefg&quot;</span>
1
</pre>
<p>In other words, the i'th element of slice j is the same as the j'th element of slice i: it is the <code><span class='Value'>i</span><span class='Function'>+</span><span class='Value'>j</span></code>'th element of the argument. So transposing still gives a possible result of Windows, but with a different slice length.</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=eyg14oaV8J2VqSniiaHijYkoM+KGlfCdlakpfSJhYmNkZWZnIg==&run">↗️</a><pre>    <span class='Brace'>{</span><span class='Paren'>(</span><span class='Number'>5</span><span class='Function'>↕</span><span class='Value'>𝕩</span><span class='Paren'>)</span><span class='Function'>≑⍉</span><span class='Paren'>(</span><span class='Number'>3</span><span class='Function'>↕</span><span class='Value'>𝕩</span><span class='Paren'>)</span><span class='Brace'>}</span><span class='String'>&quot;abcdefg&quot;</span>
1
</pre>
<p>In general, we need a more complicated transposeβ€”swapping the first set of <code><span class='Function'>β‰ </span><span class='Value'>𝕨</span></code> axes with the second set. Note again the use of Span, our slice-length to slice-number converter.</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=eygoNeKAvzbCrDLigL8yKeKGlfCdlakpIOKJoSAy4oC/M+KNiSgy4oC/MuKGlfCdlakpfSDihpU14oC/NuKAvzc=&run">↗️</a><pre>    <span class='Brace'>{</span><span class='Paren'>((</span><span class='Number'>5</span><span class='Ligature'>β€Ώ</span><span class='Number'>6</span><span class='Function'>Β¬</span><span class='Number'>2</span><span class='Ligature'>β€Ώ</span><span class='Number'>2</span><span class='Paren'>)</span><span class='Function'>↕</span><span class='Value'>𝕩</span><span class='Paren'>)</span> <span class='Function'>≑</span> <span class='Number'>2</span><span class='Ligature'>β€Ώ</span><span class='Number'>3</span><span class='Function'>⍉</span><span class='Paren'>(</span><span class='Number'>2</span><span class='Ligature'>β€Ώ</span><span class='Number'>2</span><span class='Function'>↕</span><span class='Value'>𝕩</span><span class='Paren'>)</span><span class='Brace'>}</span> <span class='Function'>↕</span><span class='Number'>5</span><span class='Ligature'>β€Ώ</span><span class='Number'>6</span><span class='Ligature'>β€Ώ</span><span class='Number'>7</span>
1
</pre>
<h2 id="applications">Applications</h2>
<p>Windows can be followed up with a reduction on each slice to give a windowed reduction. Here we take running sums of 3 values.</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=K8K0y5gz4oaVIOKfqDIsNiwwLDEsNCwz4p+p&run">↗️</a><pre>    <span class='Function'>+</span><span class='Modifier'>´˘</span><span class='Number'>3</span><span class='Function'>↕</span> <span class='Bracket'>⟨</span><span class='Number'>2</span><span class='Separator'>,</span><span class='Number'>6</span><span class='Separator'>,</span><span class='Number'>0</span><span class='Separator'>,</span><span class='Number'>1</span><span class='Separator'>,</span><span class='Number'>4</span><span class='Separator'>,</span><span class='Number'>3</span><span class='Bracket'>⟩</span>
⟨ 8 7 5 8 ⟩
</pre>
<p>A common task is to pair elements, with an initial or final element so the total length stays the same. This can also be done with a pairwise reduction, but another good way (and more performant without special support in the interpreter) is to add the element and then use windows matching the original length. Here both methods are used to invert <code><span class='Function'>+</span><span class='Modifier'>`</span></code>, which requires we take pairwise differences starting at initial value 0.</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=LcucwrTLmDLihpUw4oi+ICtgIDPigL8y4oC/MeKAvzEKKC3LnMud4omg4oaVMOKIvuKKoikgK2AgM+KAvzLigL8x4oC/MQ==&run">↗️</a><pre>    <span class='Function'>-</span><span class='Modifier'>˜´˘</span><span class='Number'>2</span><span class='Function'>↕</span><span class='Number'>0</span><span class='Function'>∾</span> <span class='Function'>+</span><span class='Modifier'>`</span> <span class='Number'>3</span><span class='Ligature'>β€Ώ</span><span class='Number'>2</span><span class='Ligature'>β€Ώ</span><span class='Number'>1</span><span class='Ligature'>β€Ώ</span><span class='Number'>1</span>
⟨ 3 2 1 1 ⟩
    <span class='Paren'>(</span><span class='Function'>-</span><span class='Modifier'>˜˝</span><span class='Function'>≠↕</span><span class='Number'>0</span><span class='Function'>∾⊒</span><span class='Paren'>)</span> <span class='Function'>+</span><span class='Modifier'>`</span> <span class='Number'>3</span><span class='Ligature'>β€Ώ</span><span class='Number'>2</span><span class='Ligature'>β€Ώ</span><span class='Number'>1</span><span class='Ligature'>β€Ώ</span><span class='Number'>1</span>
⟨ 3 2 1 1 ⟩
</pre>
<p>This method extends to any number of initial elements. We can modify the running sum above to keep the length constant by starting with two zeros.</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=KCvLneKJoOKGlSgy4qWKMCniirjiiL4pIOKfqDIsNiwwLDEsNCwz4p+p&run">↗️</a><pre>    <span class='Paren'>(</span><span class='Function'>+</span><span class='Modifier'>˝</span><span class='Function'>≠↕</span><span class='Paren'>(</span><span class='Number'>2</span><span class='Function'>β₯Š</span><span class='Number'>0</span><span class='Paren'>)</span><span class='Modifier2'>⊸</span><span class='Function'>∾</span><span class='Paren'>)</span> <span class='Bracket'>⟨</span><span class='Number'>2</span><span class='Separator'>,</span><span class='Number'>6</span><span class='Separator'>,</span><span class='Number'>0</span><span class='Separator'>,</span><span class='Number'>1</span><span class='Separator'>,</span><span class='Number'>4</span><span class='Separator'>,</span><span class='Number'>3</span><span class='Bracket'>⟩</span>
⟨ 2 8 8 7 5 8 ⟩
</pre>