diff options
| author | Marshall Lochbaum <mwlochbaum@gmail.com> | 2020-08-16 22:14:53 -0400 |
|---|---|---|
| committer | Marshall Lochbaum <mwlochbaum@gmail.com> | 2020-08-16 22:14:53 -0400 |
| commit | bda30287098ab9002bc4d3e8290ed06c21abec28 (patch) | |
| tree | 17e2c7599c0f2af08accab4363728a4ae2dbbd21 /docs/doc | |
| parent | 144854e542f6f853d10d9efed95de2d1a5025758 (diff) | |
Add document on Solo, Couple, and Merge
Diffstat (limited to 'docs/doc')
| -rw-r--r-- | docs/doc/couple.html | 84 | ||||
| -rw-r--r-- | docs/doc/index.html | 1 | ||||
| -rw-r--r-- | docs/doc/join.html | 2 | ||||
| -rw-r--r-- | docs/doc/leading.html | 4 |
4 files changed, 88 insertions, 3 deletions
diff --git a/docs/doc/couple.html b/docs/doc/couple.html new file mode 100644 index 00000000..4c65743c --- /dev/null +++ b/docs/doc/couple.html @@ -0,0 +1,84 @@ +<head><link href="../style.css" rel="stylesheet"/></head> +<div class="nav"><a href="https://github.com/mlochbaum/BQN">BQN</a></div> +<h1 id="couple-and-merge">Couple and Merge</h1> +<p>Solo/Couple (<code><span class='Function'>≍</span></code>) and Merge (<code><span class='Function'>></span></code>) are functions that create a higher-rank array from lower-rank components. Each takes some number of inner arrays organized in an outer structure, and creates a single array combining all elements of those inner arrays. For example, let's couple two arrays of shape <code><span class='Number'>2</span><span class='Ligature'>‿</span><span class='Number'>3</span></code>:</p> +<pre> <span class='Function'>⊢</span> <span class='Value'>p</span> <span class='Gets'>←</span> <span class='Number'>3</span><span class='Ligature'>‿</span><span class='Number'>5</span><span class='Function'>×</span><span class='Modifier'>⌜</span><span class='Function'>↕</span><span class='Number'>3</span> +┌─ +╵ 0 3 6 + 0 5 10 + ┘ + <span class='Function'>⊢</span> <span class='Value'>q</span> <span class='Gets'>←</span> <span class='Number'>2</span><span class='Ligature'>‿</span><span class='Number'>3</span><span class='Function'>⥊</span><span class='String'>"abcdef"</span> +┌─ +╵"abc + def" + ┘ + <span class='Value'>p</span> <span class='Function'>≍</span> <span class='Value'>q</span> <span class='Comment'># p coupled to q +</span>┌─ +╎ 0 3 6 + 0 5 10 + + 'a' 'b' 'c' + 'd' 'e' 'f' + ┘ + <span class='Function'>≢</span> <span class='Value'>p</span> <span class='Function'>≍</span> <span class='Value'>q</span> +⟨ 2 2 3 ⟩ +</pre> +<p>The result has two inner axes that are shared by <code><span class='Value'>p</span></code> and <code><span class='Value'>q</span></code>, preceded by an outer axis: length 2 because there are two arguments. Calling <code><span class='Function'>≍</span></code> with no left argument does something simpler: because there is one argument, it just adds a length-1 axis to the front. The argument goes solo, becoming the only major cell of the result.</p> +<pre> <span class='Function'>≍</span> <span class='Value'>q</span> +┌─ +╎"abc + def" + ┘ + <span class='Function'>≢</span> <span class='Function'>≍</span> <span class='Value'>q</span> +⟨ 1 2 3 ⟩ +</pre> +<p>Merge (<code><span class='Function'>></span></code>) also takes one argument, but a nested one. Its argument is an array of arrays, each with the same shape. The shape of the result is then the outer shape followed by this shared inner shape.</p> +<pre> <span class='Function'>⊢</span> <span class='Value'>a</span> <span class='Gets'>←</span> <span class='String'>"AB"</span><span class='Ligature'>‿</span><span class='String'>"CD"</span> <span class='Function'>∾</span><span class='Modifier'>⌜</span> <span class='String'>"rst"</span><span class='Ligature'>‿</span><span class='String'>"uvw"</span><span class='Ligature'>‿</span><span class='String'>"xyz"</span> +┌─ +╵ "ABrst" "ABuvw" "ABxyz" + "CDrst" "CDuvw" "CDxyz" + ┘ + <span class='Function'>></span> <span class='Value'>a</span> +┌─ +╎"ABrst + ABuvw + ABxyz + + CDrst + CDuvw + CDxyz" + ┘ + <span class='Function'>≢</span> <span class='Function'>></span> <span class='Value'>a</span> +⟨ 2 3 5 ⟩ +</pre> +<p>Merge is effectively a generalization of Solo and Couple, since Solo is <code><span class='Brace'>{</span><span class='Function'>></span><span class='Bracket'>⟨</span><span class='Value'>𝕩</span><span class='Bracket'>⟩</span><span class='Brace'>}</span></code> and Couple is <code><span class='Brace'>{</span><span class='Function'>></span><span class='Bracket'>⟨</span><span class='Value'>𝕨</span><span class='Separator'>,</span><span class='Value'>𝕩</span><span class='Bracket'>⟩</span><span class='Brace'>}</span></code>. Since <code><span class='Function'>≍</span></code> works on the "list" of arguments, it can only add one dimension, but <code><span class='Function'>></span></code> can take any number of dimensions as its input.</p> +<h2 id="merge-and-array-theory">Merge and array theory</h2> +<p>In all cases what these functions do is more like reinterpreting existing data than creating new information. In fact, if we ignore the shape and look at the ravels of the arrays involved in a call to Merge, we find that it just <a href="join.html">joins</a> them together. Essentially, Merge is a request to ensure that the inner arrays (which, being independent elements, could be any sort of "ragged" array) can fit together in an array, and then to consider them to be such an array. For this reason, Merge (or a virtual analogue) is used to combine the result cells when calling a function with Rank into a single array.</p> +<pre> <span class='Function'>⥊</span> <span class='Function'>></span> <span class='Value'>a</span> +"ABrstABuvwABxyzCDrstCDuvwCDxyz" + <span class='Function'>⥊</span> <span class='Function'>⥊</span><span class='Modifier'>¨</span> <span class='Value'>a</span> +⟨ "ABrst" "ABuvw" "ABxyz" "CDrst" "CDuvw" "CDxyz" ⟩ + <span class='Function'>∾</span> <span class='Function'>⥊</span> <span class='Function'>⥊</span><span class='Modifier'>¨</span> <span class='Value'>a</span> +"ABrstABuvwABxyzCDrstCDuvwCDxyz" +</pre> +<p>The way this happens, and the constraint that all inner arrays have the same shape, is closely connected to the concept of an array, and like Table <code><span class='Modifier'>⌜</span></code>, Merge might be considered a fundamental way to build up multidimensional arrays from lists. In both cases scalars are somewhat special. They are the identity element of a function with Table, and can be produced by Merge inverse, <code><span class='Function'>></span><span class='Modifier'>⁼</span></code> <strong>on a list</strong>, which forces either the outer or inner shape to be empty (BQN chooses <code><span class='Function'>></span><span class='Modifier'>⁼</span></code> to be <code><span class='Function'><</span></code>, but only on an array, as <code><span class='Function'>></span></code> cannot produce non-arrays). Merge has another catch as well: it cannot produce arrays with a <code><span class='Number'>0</span></code> in the shape, except at the end, without some sort of prototype system.</p> +<pre> <span class='Function'>⊢</span> <span class='Value'>e</span> <span class='Gets'>←</span> <span class='Bracket'>⟨⟩</span><span class='Modifier'>¨</span> <span class='Function'>↕</span><span class='Number'>3</span> +⟨ ⟨⟩ ⟨⟩ ⟨⟩ ⟩ + <span class='Function'>≢</span> <span class='Function'>></span> <span class='Value'>e</span> +⟨ 3 0 ⟩ + <span class='Function'>≢</span> <span class='Function'>></span> <span class='Function'>></span> <span class='Value'>e</span> +⟨ 3 0 ⟩ +</pre> +<p>Above we start with a list of three empty arrays. After merging once we get a shape <code><span class='Number'>3</span><span class='Ligature'>‿</span><span class='Number'>0</span></code> array, sure, but what happens next? The shape added by another merge is the shared shape of that array's elements—and there aren't any! If the nested list kept some type information around then we might know, but extra type information is essentially how lists pretend to be arrays. True dynamic lists simply can't represent multidimensional arrays with a <code><span class='Number'>0</span></code> in the middle of the shape. In this sense, arrays are a richer model than nested lists.</p> +<h2 id="coupling-scalars">Coupling scalars</h2> +<p>A note on the topic of Solo and Couple applied to scalars. As always, one axis will be added, so that the result is a list (strangely, J's <a href="https://code.jsoftware.com/wiki/Vocabulary/commaco#dyadic">laminate</a> differs from Couple in this one case, as it will add an axis to get a shape <code><span class='Number'>2</span><span class='Ligature'>‿</span><span class='Number'>1</span></code> result). For Solo, this is interchangeable with Deshape (<code><span class='Function'>⥊</span></code>), and either primitive might be chosen for stylistic reasons. For Couple, it is equivalent to Join-to (<code><span class='Function'>∾</span></code>), but this is an irregular form of Join-to because it is the only case where Join-to adds an axis to both arguments instead of just one. Couple should be preferred in this case.</p> +<p>The pair function, which creates a list from its arguments, can be written <code><span class='Function'>Pair</span> <span class='Gets'>←</span> <span class='Function'>≍</span><span class='Modifier2'>○</span><span class='Function'><</span></code>, while <code><span class='Function'>≍</span></code> in either valence is <code><span class='Function'>></span><span class='Modifier2'>∘</span><span class='Function'>Pair</span></code>. As an interesting consequence, <code><span class='Function'>≍</span> <span class='Gets'>←→</span> <span class='Function'>></span><span class='Modifier2'>∘</span><span class='Function'>≍</span><span class='Modifier2'>○</span><span class='Function'><</span></code>, and the same relationship holds for <code><span class='Function'>Pair</span></code>.</p> +<pre> <span class='Bracket'>⟨</span><span class='Number'>2</span><span class='Separator'>,</span><span class='Number'>3</span><span class='Bracket'>⟩</span> <span class='Function'>≍</span><span class='Modifier2'>○</span><span class='Function'><</span> <span class='String'>"abc"</span> <span class='Comment'># Pair two values +</span>⟨ ⟨ 2 3 ⟩ "abc" ⟩ + <span class='Function'>≍</span><span class='Modifier2'>○</span><span class='Function'><</span> <span class='String'>"abc"</span> <span class='Comment'># Pair one(?) value +</span>⟨ "abc" ⟩ +</pre> +<h2 id="definitions">Definitions</h2> +<p>As discussed above, <code><span class='Function'>≍</span></code> is equivalent to <code><span class='Function'>></span><span class='Brace'>{</span><span class='Bracket'>⟨</span><span class='Value'>𝕩</span><span class='Bracket'>⟩</span><span class='Value'>;</span><span class='Bracket'>⟨</span><span class='Value'>𝕨</span><span class='Separator'>,</span><span class='Value'>𝕩</span><span class='Bracket'>⟩</span><span class='Brace'>}</span></code>. To complete the picture we should describe Merge fully. Merge is defined on an array argument <code><span class='Value'>𝕩</span></code> such that there's some shape <code><span class='Value'>s</span></code> satisfying <code><span class='Function'>∧</span><span class='Modifier'>´</span><span class='Function'>⥊</span><span class='Paren'>(</span><span class='Value'>s</span><span class='Function'>≡≢</span><span class='Paren'>)</span><span class='Modifier'>¨</span><span class='Value'>𝕩</span></code>. If <code><span class='Value'>𝕩</span></code> is empty then any shape satisfies this expression; <code><span class='Value'>s</span></code> should be chosen based on known type information for <code><span class='Value'>𝕩</span></code> or otherwise assumed to be <code><span class='Bracket'>⟨⟩</span></code>. If <code><span class='Value'>s</span></code> is empty then <code><span class='Value'>𝕩</span></code> is allowed to contain non-arrays as well as array scalars, and these will be implicitly promoted to arrays by the <code><span class='Function'>⊑</span></code> indexing used later. We construct the result by combining the outer and inner axes of the argument with Table; since the outer axes come first they must correspond to the left argument and the inner axes must correspond to the right argument. <code><span class='Value'>𝕩</span></code> is a natural choice of left argument, and because no concrete array can be used, the right argument will be <code><span class='Function'>↕</span><span class='Value'>s</span></code>, the array of indices into any element of <code><span class='Value'>𝕩</span></code>. To get the appropriate element corresponding to a particular choice of index and element of <code><span class='Value'>𝕩</span></code> we should select using that index. The result of Merge is <code><span class='Value'>𝕩</span><span class='Function'>⊑</span><span class='Modifier'>˜⌜</span><span class='Function'>↕</span><span class='Value'>s</span></code>.</p> +<p>Given this definition we can also describe Rank (<code><span class='Modifier2'>⎉</span></code>) in terms of Each (<code><span class='Modifier'>¨</span></code>) and the simpler monadic function Enclose-Rank <code><span class='Function'><</span><span class='Modifier2'>⎉</span><span class='Value'>k</span></code>. We assume effective ranks <code><span class='Value'>j</span></code> for <code><span class='Value'>𝕨</span></code> (if present) and <code><span class='Value'>k</span></code> for <code><span class='Value'>𝕩</span></code> have been computed. Then the correspondence is <code><span class='Value'>𝕨</span><span class='Function'>F</span><span class='Modifier2'>⎉</span><span class='Value'>k𝕩</span> <span class='Gets'>←→</span> <span class='Function'>></span><span class='Paren'>(</span><span class='Function'><</span><span class='Modifier2'>⎉</span><span class='Value'>j𝕨</span><span class='Paren'>)</span><span class='Function'>F</span><span class='Modifier'>¨</span><span class='Paren'>(</span><span class='Function'><</span><span class='Modifier2'>⎉</span><span class='Value'>k𝕩</span><span class='Paren'>)</span></code>.</p> + diff --git a/docs/doc/index.html b/docs/doc/index.html index 2ad426ba..d70ee9b8 100644 --- a/docs/doc/index.html +++ b/docs/doc/index.html @@ -16,6 +16,7 @@ <li><a href="join.html">Join</a> (<code><span class='Function'>∾</span></code>)</li> <li><a href="logic.html">Logical functions</a> (<code><span class='Function'>∧∨¬</span></code>)</li> <li><a href="prefixes.html">Prefixes and Suffixes</a> (<code><span class='Function'>↑↓</span></code>)</li> +<li><a href="couple.html">Solo, Couple, and Merge</a> (<code><span class='Function'>≍></span></code>)</li> <li><a href="transpose.html">Transpose</a> (<code><span class='Function'>⍉</span></code>)</li> <li><a href="windows.html">Windows</a> (<code><span class='Function'>↕</span></code>)</li> </ul> diff --git a/docs/doc/join.html b/docs/doc/join.html index f6edc533..6b595caf 100644 --- a/docs/doc/join.html +++ b/docs/doc/join.html @@ -1,7 +1,7 @@ <head><link href="../style.css" rel="stylesheet"/></head> <div class="nav"><a href="https://github.com/mlochbaum/BQN">BQN</a></div> <h1 id="join">Join</h1> -<p>Join (<code><span class='Function'>∾</span></code>) 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 Merge (<code><span class='Function'>></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'>></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 (<code><span class='Function'>∾</span></code>) 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'>></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'>></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> <pre> <span class='Function'>∾</span><span class='String'>"time"</span><span class='Ligature'>‿</span><span class='String'>"to"</span><span class='Ligature'>‿</span><span class='String'>"join"</span><span class='Ligature'>‿</span><span class='String'>"some"</span><span class='Ligature'>‿</span><span class='String'>"words"</span> "timetojoinsomewords" diff --git a/docs/doc/leading.html b/docs/doc/leading.html index 0301ddd4..e86c57f7 100644 --- a/docs/doc/leading.html +++ b/docs/doc/leading.html @@ -66,7 +66,7 @@ ef" ┘ </pre> -<p>Solo (<code><span class='Function'>≍</span></code>), something of a maverick, manages to act on <em>zero</em> leading axes of its argument by creating the first axis of the <em>result</em> instead. Because it doesn't need any axis to work, it can go in front of either axis but also past the last one by working with rank 0, a case where most array functions would give an error.</p> +<p><a href="couple.html">Solo</a> (<code><span class='Function'>≍</span></code>), something of a maverick, manages to act on <em>zero</em> leading axes of its argument by creating the first axis of the <em>result</em> instead. Because it doesn't need any axis to work, it can go in front of either axis but also past the last one by working with rank 0, a case where most array functions would give an error.</p> <pre> <span class='Function'>≢</span> <span class='Function'>≍</span> <span class='Value'>a</span> <span class='Comment'># Solo adds a length-1 axis </span>⟨ 1 3 2 ⟩ <span class='Value'>a</span> <span class='Function'>≡</span> <span class='Function'>⊏</span> <span class='Function'>≍</span> <span class='Value'>a</span> <span class='Comment'># First Cell undoes this @@ -107,7 +107,7 @@ <p>The other two monadic functions that work on high-rank arguments are Deshape (<code><span class='Function'>⥊</span></code>) and First (<code><span class='Function'>⊑</span></code>). These treat the argument as one long list, ordered by its element indices. This ordering privileges leading axes (in fact, it is the reason for the choice of leading axes in the leading axis convention), but these functions can't really be said to work on leading axes: they apply to all axes.</p> <p>The Each (<code><span class='Modifier'>¨</span></code>) and Table (<code><span class='Modifier'>⌜</span></code>) modifiers return functions which are the same in the monadic case. These functions simply go through all elements of the argument array without regard for its multi-dimensional structure (the operand is applied to elements in index order, matching Deshape; this matters if it has side effects). Similarly, monadic scalar functions do not have any sort of leading axis dependence.</p> <h2 id="dyadic-functions">Dyadic functions</h2> -<p>For dyadic functions the pattern of working on only one argument axis is not so common. Only two functions can be said to follow it roughly: Join to (<code><span class='Function'>∾</span></code>) combines two arrays along one axis, using the first axis of both arguments if they have the same rank and of the higher-rank argument if they differ by one. Couple (<code><span class='Function'>≍</span></code>), like Solo, does not manipulate the argument axes but adds a result axis. There are also some functions that can't be limited to leading axes: Reshape (<code><span class='Function'>⥊</span></code>) treats the argument as one long list, and Pick (<code><span class='Function'>⊑</span></code>) requires each index to be as long as the right argument's rank, because it selects elements and not cells from the right argument.</p> +<p>For dyadic functions the pattern of working on only one argument axis is not so common. Only two functions can be said to follow it roughly: Join to (<code><span class='Function'>∾</span></code>) combines two arrays along one axis, using the first axis of both arguments if they have the same rank and of the higher-rank argument if they differ by one. <a href="couple.html">Couple</a> (<code><span class='Function'>≍</span></code>), like Solo, does not manipulate the argument axes but adds a result axis. There are also some functions that can't be limited to leading axes: Reshape (<code><span class='Function'>⥊</span></code>) treats the argument as one long list, and Pick (<code><span class='Function'>⊑</span></code>) requires each index to be as long as the right argument's rank, because it selects elements and not cells from the right argument.</p> <h3 id="multiple-axes">Multiple axes</h3> <p>Instead of always working on a single axis, many dyadic functions work on one axis by default, but also allow a left argument with multiple elements corresponding to leading axes of the right argument. To decide which of the two possibilities applies, these functions test the left argument depth, a convention that is discussed in the <a href="depth.html#testing-depth-for-multiple-axis-primitives">depth</a> documentation. A left argument that applies to one axis has a particular depth; the argument can also be a list of such arguments.</p> <table> |
