aboutsummaryrefslogtreecommitdiff
path: root/docs/doc/join.html
blob: b0f9ead457b839efa99db5ecda1cc2ed8a69409f (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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
<head>
  <link href="../favicon.ico" rel="shortcut icon" type="image/x-icon"/>
  <link href="../style.css" rel="stylesheet"/>
  <title>BQN: Join and Join To</title>
</head>
<div class="nav">(<a href="https://github.com/mlochbaum/BQN">github</a>) / <a href="../index.html">BQN</a> / <a href="index.html">doc</a></div>
<h1 id="join-and-join-to"><a class="header" href="#join-and-join-to">Join and Join To</a></h1>
<p>The glyph <code><span class='Function'></span></code> combines arrays along an existing axis, a concept that other languages might call &quot;concatenation&quot; or &quot;catenation&quot; but BQN names &quot;Join&quot;. The one-argument form Join and two-argument form Join To are parallel to <a href="couple.html">the functions</a> that combine arrays along a new axis, Merge (<code><span class='Function'>&gt;</span></code>) and Couple (<code><span class='Function'></span></code>).</p>
<h2 id="join-to"><a class="header" href="#join-to">Join To</a></h2>
<p>Join To connects its two arguments together, for example to join two strings:</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=ImFiY2QiIOKIviAiRUZHIg==">↗️</a><pre>    <span class='String'>&quot;abcd&quot;</span> <span class='Function'></span> <span class='String'>&quot;EFG&quot;</span>
"abcdEFG"
</pre>
<p>If the arguments have the same rank, then they are combined along the first axis: the result is an array whose <a href="array.html#cells">major cells</a> are the major cells of <code><span class='Value'>𝕨</span></code> followed by the major cells of <code><span class='Value'>𝕩</span></code>. For arrays with rank two or more, this means they will be joined &quot;vertically&quot; according to BQN's <a href="arrayrepr.html#array-display">display</a>.</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4oqiIGEg4oaQIDMgK+KMnOKXi+KGlSA0CuKKoiBiIOKGkCAy4oC/NCDipYog4oaVOAphIOKIviBi">↗️</a><pre>    <span class='Function'></span> <span class='Value'>a</span> <span class='Gets'></span> <span class='Number'>3</span> <span class='Function'>+</span><span class='Modifier'></span><span class='Modifier2'></span><span class='Function'></span> <span class='Number'>4</span>
┌─         
╵ 0 1 2 3  
  1 2 3 4  
  2 3 4 5  
          ┘
    <span class='Function'></span> <span class='Value'>b</span> <span class='Gets'></span> <span class='Number'>2</span><span class='Ligature'></span><span class='Number'>4</span> <span class='Function'></span> <span class='Function'></span><span class='Number'>8</span>
┌─         
╵ 0 1 2 3  
  4 5 6 7  
          ┘
    <span class='Value'>a</span> <span class='Function'></span> <span class='Value'>b</span>
┌─         
╵ 0 1 2 3  
  1 2 3 4  
  2 3 4 5  
  0 1 2 3  
  4 5 6 7  
          ┘
</pre>
<p>For this definition to work, major cells of <code><span class='Value'>𝕨</span></code> and <code><span class='Value'>𝕩</span></code> have to have the same shape. That means that <code><span class='Value'>𝕨</span><span class='Function'></span><span class='Modifier2'></span><span class='Paren'>(</span><span class='Number'>1</span><span class='Function'>↓≢</span><span class='Paren'>)</span><span class='Value'>𝕩</span></code>, and the shape of the result is the sum of the lengths of <code><span class='Value'>𝕨</span></code> and <code><span class='Value'>𝕩</span></code> followed by their shared major cell shape: to use a self-referential definition, the final shape is given by <code><span class='Function'>+</span><span class='Modifier2'></span><span class='Function'></span> <span class='Function'></span> <span class='Function'></span><span class='Modifier'></span><span class='Modifier2'></span><span class='Paren'>(</span><span class='Number'>1</span><span class='Function'>↓≢</span><span class='Paren'>)</span></code> for arguments of equal rank.</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4oqiIGEg4oaQIDMgK+KMnOKXi+KGlSA0CuKKoiBiIOKGkCAy4oC/NCDipYog4oaVOAphIOKIviAy4oC/NeKlimIgICMgU2hhcGVzIGRvbid0IGZpdA==">↗️</a><pre>    <span class='Value'>a</span> <span class='Function'></span> <span class='Number'>2</span><span class='Ligature'></span><span class='Number'>5</span><span class='Function'></span><span class='Value'>b</span>  <span class='Comment'># Shapes don't fit
</span><span class='Error'>Error: ∾: Lengths not matchable (3‿4 ≡ ≢𝕨, 2‿5 ≡ ≢𝕩)</span>
</pre>
<p>Join To will also allow arguments with ranks that are one apart. In this case, the smaller-rank argument is treated as a major cell in its entirety. If for example <code><span class='Value'>𝕨</span><span class='Function'>&lt;</span><span class='Modifier2'></span><span class='Function'>=</span><span class='Value'>𝕩</span></code>, then we must have <code><span class='Paren'>(</span><span class='Function'></span><span class='Value'>𝕨</span><span class='Paren'>)</span><span class='Function'></span><span class='Number'>1</span><span class='Function'>↓≢</span><span class='Value'>𝕩</span></code>, and the result shape is <code><span class='Number'>1</span><span class='Modifier2'></span><span class='Function'>+</span><span class='Modifier2'></span><span class='Function'>⊑≢</span><span class='Value'>𝕩</span></code>.</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4oqiIGEg4oaQIDMgK+KMnOKXi+KGlSA0CjTigL8y4oC/M+KAvzAg4oi+IGE=">↗️</a><pre>    <span class='Number'>4</span><span class='Ligature'></span><span class='Number'>2</span><span class='Ligature'></span><span class='Number'>3</span><span class='Ligature'></span><span class='Number'>0</span> <span class='Function'></span> <span class='Value'>a</span>
┌─         
╵ 4 2 3 0  
  0 1 2 3  
  1 2 3 4  
  2 3 4 5  
          ┘
</pre>
<p>An edge case for Join To is that it can also be applied to two units to make a list:</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=MyDiiL4gJ2Mn">↗️</a><pre>    <span class='Number'>3</span> <span class='Function'></span> <span class='String'>'c'</span>
⟨ 3 'c' ⟩
</pre>
<p>This case is unusual because the rank of the result is higher than that of either argument. It's also identical to Couple (<code><span class='Function'></span></code>); Couple should be preferred because it doesn't require a special case for this situation. See <a href="couple.html#coupling-units">coupling units</a>.</p>
<h2 id="join"><a class="header" href="#join">Join</a></h2>
<p>The monadic form of <code><span class='Function'></span></code>, called simply Join, is more complicated than Join To because it really takes not just one argument but an entire array of them. Join is an extension of the monadic function <a href="https://aplwiki.com/wiki/Raze">Raze</a> from A+ and J to arbitrary argument ranks. It has the same relationship to Join to, the dyadic function sharing the same glyph, as <a href="couple.html">Merge</a> (<code><span class='Function'>&gt;</span></code>) does to Couple (<code><span class='Function'></span></code>): <code><span class='Value'>a</span><span class='Function'></span><span class='Value'>b</span></code> is <code><span class='Function'>&gt;</span><span class='Value'>a</span><span class='Ligature'></span><span class='Value'>b</span></code> and <code><span class='Value'>a</span><span class='Function'></span><span class='Value'>b</span></code> is <code><span class='Function'></span><span class='Value'>a</span><span class='Ligature'></span><span class='Value'>b</span></code>. While Merge and Couple combine arrays (the elements of Merge's argument, or the arguments themselves for Couple) along a new leading axis, Join and Join to combine them along the existing leading axis. Both Merge and Join can also be called on a higher-rank array, causing Merge to add multiple leading axes while Join combines elements along multiple existing axes.</p>
<p>Join can be used to combine several strings into a single string, like <code><span class='Value'>array.join</span><span class='Paren'>()</span></code> in Javascript (but it doesn't force the result to be a string).</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4oi+InRpbWUi4oC/InRvIuKAvyJqb2luIuKAvyJzb21lIuKAvyJ3b3JkcyI=">↗️</a><pre>    <span class='Function'></span><span class='String'>&quot;time&quot;</span><span class='Ligature'></span><span class='String'>&quot;to&quot;</span><span class='Ligature'></span><span class='String'>&quot;join&quot;</span><span class='Ligature'></span><span class='String'>&quot;some&quot;</span><span class='Ligature'></span><span class='String'>&quot;words&quot;</span>
"timetojoinsomewords"
</pre>
<p>To join with a separator in between, we might prepend the separator to each string, then remove the leading separator after joining. Another approach would be to insert the separator array as an element between each pair of array elements before calling Join.</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=MeKGk+KIvicgJ+KIvsKoInRpbWUi4oC/InRvIuKAvyJqb2luIuKAvyJzb21lIuKAvyJ3b3JkcyIKCuKIvjHihpPipYooPCIgKiAiKeKJjcuYInRpbWUi4oC/InRvIuKAvyJqb2luIuKAvyJzb21lIuKAvyJ3b3JkcyI=">↗️</a><pre>    <span class='Number'>1</span><span class='Function'>↓∾</span><span class='String'>' '</span><span class='Function'></span><span class='Modifier'>¨</span><span class='String'>&quot;time&quot;</span><span class='Ligature'></span><span class='String'>&quot;to&quot;</span><span class='Ligature'></span><span class='String'>&quot;join&quot;</span><span class='Ligature'></span><span class='String'>&quot;some&quot;</span><span class='Ligature'></span><span class='String'>&quot;words&quot;</span>
"time to join some words"

    <span class='Function'></span><span class='Number'>1</span><span class='Function'>↓⥊</span><span class='Paren'>(</span><span class='Function'>&lt;</span><span class='String'>&quot; * &quot;</span><span class='Paren'>)</span><span class='Function'></span><span class='Modifier'>˘</span><span class='String'>&quot;time&quot;</span><span class='Ligature'></span><span class='String'>&quot;to&quot;</span><span class='Ligature'></span><span class='String'>&quot;join&quot;</span><span class='Ligature'></span><span class='String'>&quot;some&quot;</span><span class='Ligature'></span><span class='String'>&quot;words&quot;</span>
"time * to * join * some * words"
</pre>
<p>Join also extends the rank of a unit element (including an atom) to allow it to fit into the list. The highest-rank element determines the rank of the result.</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4oi+ImFiYyLigL8nZCfigL8iZWYi4oC/KDwnZycpCgriiL4iYWJjZCIgICMgUmVzdWx0IGhhcyB0byBiZSByYW5rIDAsIGltcG9zc2libGU=">↗️</a><pre>    <span class='Function'></span><span class='String'>&quot;abc&quot;</span><span class='Ligature'></span><span class='String'>'d'</span><span class='Ligature'></span><span class='String'>&quot;ef&quot;</span><span class='Ligature'></span><span class='Paren'>(</span><span class='Function'>&lt;</span><span class='String'>'g'</span><span class='Paren'>)</span>
"abcdefg"

    <span class='Function'></span><span class='String'>&quot;abcd&quot;</span>  <span class='Comment'># Result has to be rank 0, impossible
</span><span class='Error'>Error: ∾𝕩: 𝕩 must have an element with rank at least =𝕩</span>
</pre>
<p>Join has higher-dimensional uses as well. Given a rank-<code><span class='Value'>m</span></code> array of rank-<code><span class='Value'>n</span></code> arrays (requiring <code><span class='Value'>m</span><span class='Function'></span><span class='Value'>n</span></code>), it will merge arrays along their first <code><span class='Value'>m</span></code> axes. For example, if the argument is a matrix of matrices representing a <a href="https://en.wikipedia.org/wiki/Block_matrix">block matrix</a>, Join will give the corresponding unblocked matrix as its result.</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4oqiIG0g4oaQICgz4oC/MeKJjeKMnDTigL8y4oC/NSkg4qWKwqggMuKAvzPipYrihpU2CuKIviBtICAjIEpvaW4gYWxsIHRoYXQgdG9nZXRoZXI=">↗️</a><pre>    <span class='Function'></span> <span class='Value'>m</span> <span class='Gets'></span> <span class='Paren'>(</span><span class='Number'>3</span><span class='Ligature'></span><span class='Number'>1</span><span class='Function'></span><span class='Modifier'></span><span class='Number'>4</span><span class='Ligature'></span><span class='Number'>2</span><span class='Ligature'></span><span class='Number'>5</span><span class='Paren'>)</span> <span class='Function'></span><span class='Modifier'>¨</span> <span class='Number'>2</span><span class='Ligature'></span><span class='Number'>3</span><span class='Function'>⥊↕</span><span class='Number'>6</span>
┌─                                   
╵ ┌─          ┌─      ┌─             
  ╵ 0 0 0 0   ╵ 1 1   ╵ 2 2 2 2 2    
    0 0 0 0     1 1     2 2 2 2 2    
    0 0 0 0     1 1     2 2 2 2 2    
            ┘       ┘             ┘  
  ┌─          ┌─      ┌─             
  ╵ 3 3 3 3   ╵ 4 4   ╵ 5 5 5 5 5    
            ┘       ┘             ┘  
                                    ┘
    <span class='Function'></span> <span class='Value'>m</span>  <span class='Comment'># Join all that together
</span>┌─                       
╵ 0 0 0 0 1 1 2 2 2 2 2  
  0 0 0 0 1 1 2 2 2 2 2  
  0 0 0 0 1 1 2 2 2 2 2  
  3 3 3 3 4 4 5 5 5 5 5  
                        ┘
</pre>
<p>Axes with length 1 in the argument can also be left out, if it's done consistently for all elements in that position. One use of this is to add borders to an array, as in the multiplication table below.</p>
<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4oqiIG4g4oaQIDLigL804oC/NiDDl3vin6jwnZWXLPCdlanin6niiY3in6jwnZWoLPCdlajwnZS94oyc8J2VqeKfqX0gNeKAvzbigL834oC/OAoK4omiwqggbiAgIyBEaWZmZXJlbnQgcmFua3MgYnV0IGNvbXBhdGlibGUgc2hhcGVzCgriiL4gbg==">↗️</a><pre>    <span class='Function'></span> <span class='Value'>n</span> <span class='Gets'></span> <span class='Number'>2</span><span class='Ligature'></span><span class='Number'>4</span><span class='Ligature'></span><span class='Number'>6</span> <span class='Function'>×</span><span class='Brace'>{</span><span class='Bracket'></span><span class='Value'>𝕗</span><span class='Separator'>,</span><span class='Value'>𝕩</span><span class='Bracket'></span><span class='Function'></span><span class='Bracket'></span><span class='Value'>𝕨</span><span class='Separator'>,</span><span class='Value'>𝕨</span><span class='Function'>𝔽</span><span class='Modifier'></span><span class='Value'>𝕩</span><span class='Bracket'></span><span class='Brace'>}</span> <span class='Number'>5</span><span class='Ligature'></span><span class='Number'>6</span><span class='Ligature'></span><span class='Number'>7</span><span class='Ligature'></span><span class='Number'>8</span>
┌─                           
╵ ×         ⟨ 5 6 7 8 ⟩      
  ⟨ 2 4 6 ⟩ ┌─               
            ╵ 10 12 14 16    
              20 24 28 32    
              30 36 42 48    
                          ┘  
                            ┘

    <span class='Function'></span><span class='Modifier'>¨</span> <span class='Value'>n</span>  <span class='Comment'># Different ranks but compatible shapes
</span>┌─               
╵ ⟨⟩    ⟨ 4 ⟩    
  ⟨ 3 ⟩ ⟨ 3 4 ⟩  
                ┘

    <span class='Function'></span> <span class='Value'>n</span>
┌─               
╵ × 5  6  7  8   
  2 10 12 14 16  
  4 20 24 28 32  
  6 30 36 42 48  
                ┘
</pre>
<p>Even with the extension, Join has fairly strict requirements on the shapes of its argument elements—although less strict than those of Merge, which requires they all have identical shape. Suppose the argument to Join has rank <code><span class='Value'>m</span></code>. The highest element rank (call it <code><span class='Value'>n</span></code>) must be at least <code><span class='Value'>m</span></code>. The trailing shapes <code><span class='Paren'>(</span><span class='Function'>-</span><span class='Value'>n</span><span class='Function'>-</span><span class='Value'>m</span><span class='Paren'>)</span><span class='Function'></span><span class='Modifier2'></span><span class='Function'></span><span class='Modifier'>¨</span><span class='Value'>𝕩</span></code> must all be identical (the trailing shape <code><span class='Paren'>(</span><span class='Function'>-</span><span class='Value'>n</span><span class='Function'>-</span><span class='Value'>m</span><span class='Paren'>)</span><span class='Function'>↑≢∾</span><span class='Value'>𝕩</span></code> of the result will match these shapes as well). The other entries in the leading shapes need not be the same, but the shape of an element along a particular axis must depend only on the location of the element along that axis in the full array. For a list argument this imposes no restriction, since the one leading shape element is allowed to depend on position along the only axis. But for higher ranks the structure quickly becomes more rigid.</p>
<p>To state this requirement more formally in BQN, we say that there is some list <code><span class='Value'>s</span></code> of lists of lengths, so that <code><span class='Paren'>(</span><span class='Function'></span><span class='Modifier'>¨</span><span class='Value'>s</span><span class='Paren'>)</span><span class='Function'>≡≢</span><span class='Value'>𝕩</span></code>. We require element <code><span class='Value'>i</span><span class='Function'></span><span class='Value'>𝕩</span></code> to have shape <code><span class='Value'>i</span><span class='Function'></span><span class='Modifier'>¨</span><span class='Value'>s</span></code>. Then the first <code><span class='Value'>m</span></code> axes of the result are <code><span class='Function'>+</span><span class='Modifier'>´¨</span><span class='Value'>s</span></code>. To handle omitted axes, we change <code><span class='Value'>s</span></code> to contain lists of length 0 or 1 instead of lengths, and require <code><span class='Value'>i</span><span class='Function'></span><span class='Value'>𝕩</span></code> to have shape <code><span class='Function'></span><span class='Value'>i</span><span class='Function'></span><span class='Modifier'>¨</span><span class='Value'>s</span></code> instead. In the result, an omitted axis behaves exactly like a length-1 axis, so the result can be found using shapes derived from <code><span class='Number'>1</span><span class='Function'></span><span class='Modifier'>´¨¨</span><span class='Value'>s</span></code>.</p>