diff options
| author | Marshall Lochbaum <mwlochbaum@gmail.com> | 2022-06-27 22:00:55 -0400 |
|---|---|---|
| committer | Marshall Lochbaum <mwlochbaum@gmail.com> | 2022-06-27 22:00:55 -0400 |
| commit | 8389e763344637c01d0d7161091e5f2cd9b14251 (patch) | |
| tree | 5406113f6711c6b1222650228cba453c2e4641fa /doc/couple.md | |
| parent | b6185d5029e2adcc721c0cc2097f591d9a09f135 (diff) | |
Yet still more editing
Diffstat (limited to 'doc/couple.md')
| -rw-r--r-- | doc/couple.md | 26 |
1 files changed, 16 insertions, 10 deletions
diff --git a/doc/couple.md b/doc/couple.md index e62e581c..e3823249 100644 --- a/doc/couple.md +++ b/doc/couple.md @@ -2,41 +2,47 @@ # Couple and Merge -Solo/Couple (`≍`) and Merge (`>`) 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 `2‿3`: +Solo/Couple (`≍`) and Merge (`>`) are functions that build a higher-rank array out of [cells](array.md#cells). Each takes some input arrays with the same shape, and combines them into a single array that includes all their elements. For example, let's couple two arrays of shape `2‿3`: + + ⟨p←3‿5×⌜↕3, q←2‿3⥊"abcdef"⟩ # Shown side by side - ⊢ p ← 3‿5×⌜↕3 - ⊢ q ← 2‿3⥊"abcdef" p ≍ q # p coupled to q + ≢ p ≍ q -The result has two inner axes that are shared by `p` and `q`, preceded by an outer axis: length 2 because there are two arguments. Calling `≍` 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. +The result is an array with `p` and `q` for its major cells. It has two inner axes that are shared by `p` and `q`, preceded by an outer axis, with length 2 because there are two arguments. With no left argument, `≍` does something simpler: it just adds an axis of length 1 to the front. The argument goes solo, becoming the only major cell of the result. ≍ q + ≢ ≍ q -Merge (`>`) 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. +Merge (`>`) takes one argument, but a nested one. `𝕩` 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. ⊢ a ← "AB"‿"CD" ∾⌜ "rst"‿"uvw"‿"xyz" + > a + + ≢ a ≢ > a -Merge is effectively a generalization of Solo and Couple, since Solo is `{>⟨𝕩⟩}` and Couple is `{>⟨𝕨,𝕩⟩}`. Since `≍` works on the "list" of arguments, it can only add one dimension, but `>` can take any number of dimensions as its input. +Merge serves as a generalization of Solo and Couple, since Solo is `{>⟨𝕩⟩}` and Couple is `{>⟨𝕨,𝕩⟩}`. Since `≍` works on the "list" of arguments, it can only add one dimension, but `>` can take any number of dimensions as its input. ## Merge and array theory -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 deshaped arrays involved in a call to Merge, we find that it just [joins](join.md) 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. +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 deshaped arrays involved in a call to Merge, we find that it just [joins](join.md) them together. Essentially, Merge is a request to ensure that the inner arrays make up a homogeneous (not "ragged") array, and then to consider them to be such an array. It's the same thing [Rank](rank.md) does to combine the result cells from its operand into a single array. ⥊ > a + ⥊ ⥊¨ a ∾ ⥊ ⥊¨ a -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](map.md#table) `⌜`, Merge might be considered a fundamental way to build up multidimensional arrays from lists. In both cases rank-0 or [unit](enclose.md#whats-a-unit) arrays are somewhat special. They are the [identity value](fold.md#identity-values) of a function with Table, and can be produced by Merge [inverse](undo.md), `>⁼` **on a list**, which forces either the outer or inner shape to be empty (BQN chooses `>⁼` to be `<`, but only on an array, as `>` cannot produce non-arrays). Merge has another catch as well: it cannot produce arrays with a `0` in the shape, except at the end, without relying on a [fill](fill.md) element. +Somewhat like [Table](map.md#table) `⌜`, Merge might be considered a fundamental way to build up multidimensional arrays from lists. In both cases rank-0 or [unit](enclose.md#whats-a-unit) arrays are somewhat special. They are the [identity value](fold.md#identity-values) of a function with Table, and [Enclose](enclose.md) (`<`), which creates a unit, is a right inverse to Merge. Enclose is needed because Merge can't produce a rank 0 array on its own. Merge has another catch as well: it can't produce arrays with a 0 in the shape, except at the end, without relying on a [fill](fill.md) element. ⊢ e ← ⟨⟩¨ ↕3 ≢ > e ≢ > > e -Above we start with a list of three empty arrays. After merging once we get a shape `3‿0` 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 `0` in the middle of the shape. In this sense, arrays are a richer model than nested lists. +Above we start with a list of three empty arrays. After merging once we get a shape `3‿0` 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 0 in the middle of the shape. In this sense, arrays are a richer model than nested lists. ## Coupling units @@ -46,6 +52,6 @@ The function [Pair](pair.md) (`⋈`) can be written `≍○<`, while `≍` in ei ## Definitions -As discussed above, `≍` is equivalent to `>{⟨𝕩⟩;⟨𝕨,𝕩⟩}`. To complete the picture we should describe Merge fully. Merge is defined on an array argument `𝕩` such that there's some shape `s` satisfying `∧´⥊(s≡≢)¨𝕩`. If `𝕩` is empty then any shape satisfies this expression; `s` should be chosen based on known type information for `𝕩` or otherwise assumed to be `⟨⟩`. If `s` is empty then `𝕩` is allowed to contain atoms as well as unit arrays, and these will be implicitly promoted to arrays by the `⊑` 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. `𝕩` is a natural choice of left argument, and because no concrete array can be used, the right argument will be `↕s`, the array of indices into any element of `𝕩`. To get the appropriate element corresponding to a particular choice of index and element of `𝕩` we should select using that index. The result of Merge is `𝕩⊑˜⌜↕s`. +As discussed above, `≍` is equivalent to `>{⟨𝕩⟩;⟨𝕨,𝕩⟩}` or `>⋈`. To complete the picture we should describe Merge fully. Merge is defined on an array argument `𝕩` such that there's some shape `s` satisfying `∧´⥊(s≡≢)¨𝕩`. If `𝕩` is empty then any shape satisfies this expression; `s` should be chosen based on known type information for `𝕩` or otherwise assumed to be `⟨⟩`. If `s` is empty then `𝕩` is allowed to contain atoms as well as unit arrays, and these will be implicitly promoted to arrays by the `⊑` 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. `𝕩` is a natural choice of left argument, and because no concrete array can be used, the right argument will be `↕s`, the array of indices into any element of `𝕩`. To get the appropriate element corresponding to a particular choice of index and element of `𝕩` we should select using that index. The result of Merge is `𝕩⊑˜⌜↕s`. Given this definition we can also describe Rank (`⎉`) in terms of Each (`¨`) and the simpler monadic function Enclose-Rank `<⎉k`. We assume effective ranks `j` for `𝕨` (if present) and `k` for `𝕩` have been computed. Then the correspondence is `𝕨F⎉k𝕩 ←→ >(<⎉j𝕨)F¨(<⎉k𝕩)`. |
