From 8342ba5e9392811dbc0514a97e847a44a5b330a2 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Mon, 6 Jun 2022 21:29:06 -0400 Subject: When I wrote all these docs did I really understand I'd have to edit them? --- doc/glossary.md | 1 + doc/prefixes.md | 35 +++++++++++++++++++++++------------ doc/primitive.md | 14 +++++++------- doc/range.md | 14 +++++++------- doc/repeat.md | 8 ++++---- doc/replicate.md | 8 ++++---- doc/reshape.md | 18 +++++++++--------- doc/reverse.md | 16 ++++++++-------- 8 files changed, 63 insertions(+), 51 deletions(-) (limited to 'doc') diff --git a/doc/glossary.md b/doc/glossary.md index 14968154..49ce50e7 100644 --- a/doc/glossary.md +++ b/doc/glossary.md @@ -67,6 +67,7 @@ The possible roles are: * **Table**: An array of rank 2. * [**Index**](indices.md): One of a variety of ways to select an element, cell, axis, or position along an axis of an array. +* [**Index order**](reshape.md): The standard ordering of array elements by increasing index. * [**Index list**](indices.md#element-indices): A list of numbers indicating a single element of an array. ## Operations diff --git a/doc/prefixes.md b/doc/prefixes.md index 0d37e8c8..b3930ed6 100644 --- a/doc/prefixes.md +++ b/doc/prefixes.md @@ -2,23 +2,27 @@ # Prefixes and Suffixes -The Prefixes (`↑`) function gives a list of all prefixes of its argument array along the [first axis](leading.md), and Suffixes (`↓`) gives a similar list for suffixes. Because the result can be much larger than the argument, these functions may not be used often in high-performance code, but they are a powerful conceptual tool and can make sense for algorithms that are inherently quadratic. +The Prefixes (`↑`) function gives a list of all prefixes of its argument array along the [first axis](leading.md), and Suffixes (`↓`) gives a similar list for suffixes. Because the result can be much larger than the argument, these functions may not be used often in high-performance code, but they are a good conceptual tool and can make sense for algorithms that are inherently quadratic. ↑ "abcde" + ↓ "abcde" The functions are closely related to [Take and Drop](take.md), as we might expect from their glyphs. Element `i⊑↑𝕩` is `i↑𝕩`, and `i⊑↓𝕩` is `i↓𝕩`. -In both cases, an empty array and the entire argument are included in the result, meaning its length is one more than that of the argument. Using [Span](logic.md), we can say that the result has elements whose lengths go from `0` to `≠𝕩`, inclusive, so there are `(≠𝕩)¬0` or `1+≠𝕩` elements. The total number or cells in the result (for example, `≠∾↑𝕩` or `+´≠¨↑𝕩`) scales with the square of the argument length—it is quadratic in `≠𝕩`. We can find the exact total by looking at Prefixes and Suffixes together: +In both cases, an empty array and the entire argument are included in the result, meaning its length is one more than that of the argument. Using [Span](logic.md), we can say that the result has elements whose [lengths](shape.md) go from `0` to `≠𝕩`, inclusive, so there are `(≠𝕩)¬0` or `1+≠𝕩` elements. The total number of cells in the result (for example, `≠∾↑𝕩` or `+´≠¨↑𝕩`) scales with the square of the argument length—it's quadratic in `≠𝕩`. We can find the exact total by looking at Prefixes and Suffixes together: (↑ ≍˘ ↓) "abcde" + (↑ ∾¨ ↓) "abcde" -Joining corresponding elements of `↑𝕩` and `↓𝕩` gives `𝕩` again. This is because `i⊑(↑∾¨↓)𝕩` is `(i⊑↑𝕩)∾(i⊑↓𝕩)`, or, using the Take and Drop correspondences, `(i↑𝕩)∾(i↓𝕩)`, which is `𝕩`. Element-wise, we are combining the first `i` cells of `𝕩` with all but the first `i`. Looking at the entire result, we now know that `(↑∾¨↓)𝕩` is `(1+≠𝕩)⥊<𝕩`. The total number of cells in this combined array is therefore `(1+≠𝕩)×≠𝕩`, or `1⊸+⊸×≠𝕩`. Each of Prefixes and Suffixes must have the same total number of cells (in fact, `↑𝕩` is `⌽¨∘↓⌾⌽𝕩`, and Reverse doesn't change an array's shape). So the total number in either one is `2÷˜1⊸+⊸×≠𝕩`. With `n←≠𝕩`, it is `2÷˜n×1+n`. +Joining corresponding elements of `↑𝕩` and `↓𝕩` gives `𝕩` again. This is because `i⊑(↑∾¨↓)𝕩` is `(i⊑↑𝕩)∾(i⊑↓𝕩)`, or, using the Take and Drop correspondences, `(i↑𝕩)∾(i↓𝕩)`, which is `𝕩`. Element-wise, we are combining the first `i` cells of `𝕩` with all but the first `i`. + +Looking at the entire result, we now know that `(↑∾¨↓)𝕩` is `(1+≠𝕩)⥊<𝕩`. The total number of cells in this combined array is therefore `(1+n)×n`, setting `n←≠𝕩`. Each of Prefixes and Suffixes must have the same total number of cells (in code, `↑𝕩` is `⌽¨∘↓⌾⌽𝕩`, and Reverse doesn't change an array's shape). So the total number in either one is `2÷˜n×1+n`. ## Definition -Knowing the [length](shape.md) and the elements, it's easy to define functions for Prefixes and Suffixes: `↑` is equivalent to `(↕1+≠)↑¨<` while `↓` is `(↕1+≠)↓¨<`. Each primitive is defined only on arrays with at least one axis. +Knowing the length and the elements, it's easy to define functions for Prefixes and Suffixes: `↑` is equivalent to `(↕1+≠)↑¨<` while `↓` is `(↕1+≠)↓¨<`. Each primitive is defined only on arrays with at least one axis. ## Working with pairs @@ -29,34 +33,41 @@ Sometimes it's useful to apply an operation to every unordered pair of elements It's easy enough to use the [Table](map.md#table) modifier here, but it also computes most products twice. If we only care about the unique products, we could multiply each number by all the ones after it. "After" sounds like suffixes, so let's look at those: 1+↕6 + ↓ 1+↕6 We want to include the diagonal, so we'll pair each element with the corresponding element of the suffixes, reducing the suffixes to the original array's length by dropping the last element, which is empty. In other cases, we might not want to include it and we should instead drop the first element. (⊢ × ≠ ↑ ↓) 1+↕6 + (⊢ × 1 ↓ ↓) 1+↕6 -By using [Couple](couple.md) (`≍`) instead of `×`, we can see the argument ordering, demonstrating that we are looking at the upper right half of the matrix produced by Table. While in this case we could use `≍⚇0` to mimic the pervasion of `×`, we'd like this to work even on nested arguments so we should figure out how the mapping structure works to apply Each appropriately. +By using [Pair](pair.md) (`⋈`) instead of `×`, we can see the argument ordering, demonstrating that we are looking at the upper right half of the matrix produced by Table. While in this case we could use `⋈⚇0` to mimic the pervasion of `×`, we'd like this to work even on nested arguments so we should figure out how the mapping structure works to apply Each appropriately. - ≍⌜˜ "abc" - (<˘ ≍¨¨ ≠ ↑ ↓) "abc" + ⋈⌜˜ "abc" -As before, we can exclude the diagonal, and using Prefixes instead of Suffixes gives us the lower left half instead of the upper right—in terms of the arguments given to `≍` it reverses the argument pairs and iterates in a different order. + (<˘ ⋈¨¨ ≠ ↑ ↓) "abc" - (<˘ ≍¨¨ 1 ↓ ↓) "abc" - (<˘ ≍¨¨ 1 ↓ ↑) "abc" - (<˘ ≍¨¨ ≠ ↑ ↑) "abc" +As before, we can exclude the diagonal, and using Prefixes instead of Suffixes gives us the lower left half instead of the upper right—in terms of the arguments given to `⋈` it reverses the argument pairs and iterates in a different order. + + (<˘ ⋈¨¨ 1 ↓ ↓) "abc" + + (<˘ ⋈¨¨ 1 ↓ ↑) "abc" + + (<˘ ⋈¨¨ ≠ ↑ ↑) "abc" ## Slices -Prefixes and Suffixes give certain restricted slices of the argument array, where a slice is a contiguous selection of cells. If we want all slices along the first axis, we can take the suffixes of each prefix, or vice-versa: +Prefixes and Suffixes give certain restricted slices of the argument array, where a slice is a contiguous selection of major cells. If we want all slices along the first axis, we can take the suffixes of each prefix, or vice-versa: ↓¨↑ "abc" + ↑¨↓ "abc" Effectively, this parametrizes the slices either by ending then starting index, or by starting index then length. Four empty slices are included because in a list of length 3 there are 4 places an empty slice can start: all the spaces between or outside elements (these also correspond to all the possible positions for the result of [Bins](order.md#bins)). The slices can also be parametrized by length and then starting index using [Windows](windows.md). ((↕1+≠)↕¨<) "abc" + ((↕1+≠)<˘∘↕¨<) "abc" # Split them to match Prefixes/Suffixes We might view a slice as a selection for not two but *three* parameters: the number of cells before, in, and after the slice. The conditions are that each parameter, being a length, is at least 0, and the total of the three parameters is equal to the array length. With three parameters and one equality constraint, the space of slices is two-dimensional; the above ways to enumerate it each pick two parameters and allow the third to be dependent on these two. If you're familiar with [barycentric coordinates](https://en.wikipedia.org/wiki/Barycentric_coordinate_system) on a triangle, this should sound very familiar because that's exactly what the three parameters are! diff --git a/doc/primitive.md b/doc/primitive.md index fd514390..57c58c6e 100644 --- a/doc/primitive.md +++ b/doc/primitive.md @@ -2,9 +2,9 @@ # BQN primitives -*Primitives* are the basic functions and modifiers built into the language, written with individual glyphs (more about the concept [here](../commentary/primitive.md)). The role of a primitive when written always matches its type (but you can use its value in other roles by assigning it, or other methods). +*Primitives* are the basic functions and modifiers built into the language, written with individual glyphs (more about the concept [here](../commentary/primitive.md)). The [role](expression.md#syntactic-role) of a primitive when written always matches its type (but you can use its value in other roles by assigning it, or other methods). -Primitives have no side effects other than errors, and can't perform infinite computations, except when a primitive modifier calls an operand function that does one of these things (this can only happen when arguments are passed, as primitive modifiers are always deferred). Side effects here include both writing state such as variables or printed output, and reading any outside state, so that a function without them always returns the same result if passed the same arguments. Since trains and list notation have the same nice properties, tacit code written entirely with primitives, trains, and lists always describes finite, self-contained computations. +Primitives have no side effects other than errors, and can't perform infinite computations, except when a primitive modifier calls an operand function that does one of these things (and this can only happen when arguments are passed, as primitive modifiers are always deferred). Side effects here include both writing state such as variables or printed output, and reading any outside state, so that a function without them always returns the same result if passed the same arguments. Since trains and list notation have the same nice properties, [tacit](tacit.md) code written entirely with primitives, trains, and lists always describes finite, self-contained computations. Recursion is the primary way to perform potentially infinite computations in BQN, and it can be packaged into [control structures](control.md) like `While` for ease of use. A given BQN implementation might also provide [system values](../spec/system.md) for "impure" tasks like file access or other I/O. @@ -63,7 +63,7 @@ A function call with one argument (prefix) is called "monadic" and one with two -*Combinators* only control the application of functions. Because a non-function operand applies as a constant function, some combinators have extra meanings when passed a constant. For example, `0˜` is identical to `0˙`—a constant function that always returns 0—and `0⊸<` is the function that tests whether its right argument is greater than 0. +*Combinators* only control the application of functions, which are passed as operands. A data value such as a number or array can also be an operand and, as always, applies as a constant function. Glyph | Name(s) | Definition | Description ------|-------------------------|--------------------------------|--------------------------------------- @@ -73,13 +73,14 @@ Glyph | Name(s) | Definition | Description `○` | [Over](compose.md) | `{(𝔾𝕨)𝔽𝔾𝕩}` | Apply `𝔾` to each argument and `𝔽` to the results `⊸` | [Before/Bind](hook.md) | `{(𝔽𝕨⊣𝕩)𝔾𝕩}` | `𝔾`'s left argument comes from `𝔽` `⟜` | [After/Bind](hook.md) | `{(𝕨⊣𝕩)𝔽𝔾𝕩}` | `𝔽`'s right argument comes from `𝔾` -`⌾` | [Under](under.md) | `{𝔾⁼∘𝔽○𝔾}` OR `{(𝔾𝕩)↩𝕨𝔽○𝔾𝕩⋄𝕩}` | Apply `𝔽` over `𝔾`, then undo `𝔾` `⊘` | [Valences](valences.md) | `{𝔽𝕩;𝕨𝔾𝕩}` | Apply `𝔽` if there's one argument but `𝔾` if there are two `◶` | [Choose](choose.md) | `{f←(𝕨𝔽𝕩)⊑𝕘 ⋄ 𝕨F𝕩}` | Select one of the functions in list `𝕘` based on `𝔽` +`⌾` | [Under](under.md) | `{𝔾⁼∘𝔽○𝔾}` OR `{(𝔾𝕩)↩𝕨𝔽○𝔾𝕩⋄𝕩}` | Apply `𝔽` over `𝔾`, then undo `𝔾` +`⎊` | [Catch](assert.md#catch)| `{𝕨𝔽𝕩… 𝕨𝔾𝕩}` | Apply `𝔽`, but if it fails catch the error and apply `𝔾` -Choose isn't really a combinator since it calls the function `⊑`, and Under is not a true combinator since it has an "undo" step at the end. This step might be implemented using the left operand's inverse (*computational* Under) or its structural properties (*structural* Under). +The last three are combinators in spirit but go beyond the actual strict definition: Choose calls the function `⊑`, Under has an "undo" step at the end, and Catch traps an error. The second definition for Under and the one for Catch are written in pseudo-BQN because they can't be expressed otherwise. -Other modifiers control array traversal and iteration. In three cases a simpler 1-modifier is paired with a generalized 2-modifier: in each case the 1-modifier happens to be the same as the 2-modifier with a right operand of `¯1`. +Other modifiers control array traversal and iteration. In three cases a simpler 1-modifier is paired with a generalized 2-modifier: for each of these the 1-modifier happens to be the same as the 2-modifier with a right operand of `¯1`. | 1-Modifier | Name | 2-Modifier | Name |------------|---------------------------------------|------------|-------- @@ -90,4 +91,3 @@ Other modifiers control array traversal and iteration. In three cases a simpler | `´` | [Fold](fold.md) | | `˝` | [Insert](fold.md) | | `` ` `` | [Scan](scan.md) | -| | | `⎊` | [Catch](assert.md#catch) diff --git a/doc/range.md b/doc/range.md index 40464bbf..6a4bf535 100644 --- a/doc/range.md +++ b/doc/range.md @@ -2,7 +2,7 @@ # Range -Range (`↕`) is a monadic function that creates arrays of [indices](indices.md) (like APL's famous [iota](https://aplwiki.com/wiki/Index_Generator) function). Each element in the result is its own index. +Range (`↕`) is a monadic function that creates arrays of indices, like APL's famous [iota](https://aplwiki.com/wiki/Index_Generator) function `⍳`. Each element in the result is its own [index](indices.md). ↕ 6 @@ -20,7 +20,7 @@ The two kinds of index correspond to BQN's two selection functions: [Select](sel (↕⟨6⟩) ⊑ " pick " -They also correspond to Length (`≠`) and [Shape](shape.md) (`≢`): for an array `a`, `↕≠a` gives the indices of major cells, while `↕≢a` gives the indices of all elements. +They also correspond to [Length](shape.md) (`≠`) [and Shape](shape.md) (`≢`): for an array `a`, `↕≠a` gives the indices of major cells, while `↕≢a` gives the indices of all elements. a ← 4‿2⥊@ @@ -38,11 +38,11 @@ Calling `↕` on an atom, which must be a natural number, is the more common cas 2 ↓ ↕4 -The result of `↕𝕩` is a list of length `𝕩`, but doesn't include `𝕩` itself. That's just how counting starting at 0 works. It does mean we can create a length-0 list easily: +The result of `↕𝕩` is a list of length `𝕩`, but doesn't include `𝕩` itself. That's just how counting starting at 0 works (but a nice trick if you do want to include `𝕩` is `↕⊸∾𝕩`). It means we can create a length-0 list easily: ↕ 0 -Like all other results of `↕` on a number, `↕0` has a fill of 0. +As with any other number argument, `↕0` has a [fill](fill.md) of 0. 4 ↑ ↕0 @@ -54,7 +54,7 @@ Adding a character to a range produces a character range, with space as the fill »⍟3 'b'+↕8 -One interesting use of Range is to find, at each position in a boolean list, the most recent index that has a 1. To do this, first get the array of indices for `b`, `↕≠b`. Then multiply `b`, reducing indices where a `0` is found to 0. +One interesting use of Range is to find, at each position in a boolean list, the most recent index that has a 1. To do this, first get the array of indices for `b`, `↕≠b`. Then multiply by `b`, reducing indices where a `0` is found to 0. ⊢ b ← 0‿1‿1‿0‿0‿0‿1‿0 @@ -74,11 +74,11 @@ Where there aren't any previous 1s, this returns an index of 0, which is the sam ## List range -When the argument is a list of numbers, the result is an array of lists. +When `𝕩` is a list of numbers, the result is an array of lists. ↕ 2‿3‿4 -This array, which contains all possible choices of one natural number less than each element of `𝕩`, can also be produced using Range on numbers only, along with [Table](map.md#table) (`⌜`). +This array, which contains all possible choices of a natural number below each element of `𝕩`, can also be produced using Range on numbers only, along with [Table](map.md#table) (`⌜`). (<⟨⟩) ∾⌜´ ↕¨ 2‿3‿4 diff --git a/doc/repeat.md b/doc/repeat.md index bb7000ea..2c357acd 100644 --- a/doc/repeat.md +++ b/doc/repeat.md @@ -8,7 +8,7 @@ Repeat (`⍟`) is a 2-modifier that applies its operand function `𝔽` multiple »⍟3 "ABCDE" -In mathematics (which unsurpisingly tends to use complicated terms to talk about an easy concept), this kind of repetition is called an [iterated function](https://en.wikipedia.org/wiki/Iterated_function) and written with exponential notation. It's related to function composition `∘` in the same way that exponentiation (`⋆`) relates to multiplication (`×`): function iteration is repeated composition. +In mathematics (which unsurpisingly tends to use complicated terms to talk about an easy concept), this kind of repetition is called an [iterated function](https://en.wikipedia.org/wiki/Iterated_function) and written with exponential notation. It's related to function [composition](compose.md) `∘` in the same way that exponentiation (`⋆`) relates to multiplication (`×`): function iteration is repeated composition. n⋆4 ←→ n×n×n×n F⍟4 ←→ F∘F∘F∘F @@ -24,7 +24,7 @@ If `𝕨` is given, it's passed as the left argument to `𝔽` for every invocat 3 +⍟2 7 3 + 3 + 7 -This kind of composition can't be represented by `∘` anymore (you'd need a [train](train.md)), but it's similar in spirit. `𝕨 𝔽⍟n 𝕩` is always equivalent to `𝕨⊸𝔽⍟n 𝕩`, provided `n` is a constant—not a function, as discussed in the next section. +This kind of composition can't be represented by `∘` anymore (you'd need a [train](train.md)), but it's not much of a leap. `𝕨 𝔽⍟n 𝕩` is always equivalent to `𝕨⊸𝔽⍟n 𝕩`, provided `n` is a constant—not a function, as discussed in the next section. ## Dynamic repetition count @@ -44,11 +44,11 @@ If `𝕨` is given, then `𝔾` gets it as a left argument (to avoid this, use ` ## Negative repetition -What does it mean to repeat a function a negative number of times? For a negative integer `-n`, BQN defines `F⍟(-n)` to be `F⁼⍟n`. In particular, `F⍟¯1` simply undoes `F`. +What does it mean to repeat a function a negative number of times? For a negative integer `-n`, BQN defines `F⍟(-n)` to be `F⁼⍟n`. In particular, `F⍟¯1` simply [undoes](undo.md) `F`. 1 ⌽⍟¯1 "abcde" # Rotate backwards -Because BQN's Undo is a little looser than a strict mathematical inverse, this is an extension of the function inverse written f⁻¹ in mathematics. As a result, it doesn't have all the same properties. For natural numbers, Repeat follows the rule that `F⍟m F⍟n 𝕩` is `F⍟(m+n) 𝕩`. For integers, we have `𝕩 ≡ F⍟n F⍟(-n) 𝕩`, but not necessarily `𝕩 ≡ F⍟(-n) F⍟n 𝕩`. +Because BQN's Undo is a little looser than a strict mathematical inverse, this is an extension of the function inverse written f⁻¹ in mathematics. As a result, it doesn't have all the same properties. For natural numbers, Repeat follows the rule that `F⍟m F⍟n 𝕩` is `F⍟(m+n) 𝕩`. With a negative, we have `𝕩 ≡ F⍟n F⍟(-n) 𝕩`, but not necessarily `𝕩 ≡ F⍟(-n) F⍟n 𝕩`. ## Array of repetition counts diff --git a/doc/replicate.md b/doc/replicate.md index 66905116..47ae1eea 100644 --- a/doc/replicate.md +++ b/doc/replicate.md @@ -43,7 +43,7 @@ BQN doesn't have any of the various features used in APL to add fills to the res ## Replicate -Given a list of natural numbers `𝕨`, Replicate repeats each major cell in `𝕩` the corresponding number of times. That is, `𝕨` and `𝕩` must have the same length, and the result includes `i⊑𝕨` copies of each cell `i⊏𝕩`, in order. +Given a list of natural numbers `𝕨`, Replicate repeats each [major cell](array.md#cells) in `𝕩` the corresponding number of times. That is, `𝕨` and `𝕩` must have the same length, and the result includes `i⊑𝕨` copies of each cell `i⊏𝕩`, in order. 2‿1‿0‿2 / "abcd" @@ -65,7 +65,7 @@ When `𝕨` is a list of booleans, a cell is never repeated more than once, mean Here `≤⟜'i'` is a pervasive function, so there's no need to add `¨`. Similarly, to filter major cells of an array, `Fn˘⊸/` could be used, applying `Fn` to one major cell at a time. -A similar pattern applies to Replicate as well. The function below tests which input characters are double quotes, but by adding one it changes the result to 1 for each non-quote character and 2 for quotes (but source code and display also double quotes here, so the input string has only two `"`s and the output has four). +This idea extends to Replicate as well. The function below tests which input characters are double quotes, but by adding one it changes the result to 1 for each non-quote character and 2 for quotes (but source code and display also double quotes here, so the input string has only two `"`s and the output has four). {1+'"'=𝕩}⊸/ "for ""escaping"" quotes" @@ -95,7 +95,7 @@ If `𝕨` is `⟨⟩`, then it has depth 1, but is handled with the multidimensi ## Indices -The monadic form of `/` is much simpler than the dyadic one, with no multidimensional case or mismatched argument ranks. `𝕩` must be a list of natural numbers, and `/𝕩` is the list `𝕩/↕≠𝕩`. Its elements are the [indices](indices.md) for `𝕩`, with index `i` repeated `i⊑𝕩` times. +The monadic form of `/` is much simpler than the dyadic one, with no multidimensional case or mismatched argument ranks. `𝕩` has to be a list of natural numbers, and `/𝕩` is the list `𝕩/↕≠𝕩`. Its elements are the [indices](indices.md) for `𝕩`, with index `i` repeated `i⊑𝕩` times. / 3‿0‿1‿2 @@ -125,7 +125,7 @@ So now we have the indices of each transition from 0 to 1 or 1 to 0, in an exten -˜`˘ ∘‿2⥊/ 0(∾≠∾˜) 0‿1‿1‿1‿0‿0‿1‿0‿1‿1‿0 -This means the transitions can be grouped exactly in pairs, the beginning and end of each group. Reshape with a [computed length](reshape.md#computed-lengths) `∘‿2` groups these pairs, and then a scan ``-˜`˘`` can be used to convert the start/end format to start/length if wanted. +This means the transitions can be grouped exactly in pairs, the beginning and end of each group. Reshape with a [computed length](reshape.md#computed-lengths) `∘‿2` groups these pairs, and then a [scan](scan.md) ``-˜`˘`` can be used to convert the start/end format to start/length if wanted. ### Inverse diff --git a/doc/reshape.md b/doc/reshape.md index 38bbe203..b4081a53 100644 --- a/doc/reshape.md +++ b/doc/reshape.md @@ -36,9 +36,9 @@ bp ← ⥊⌽(20×1.5‿¯1) (+⌾⊑ ≍ -⊸≍∘⊣)˘ 29‿21-⊸≍⊸+⍉ ⟩ --> -The glyph `⥊` indicates BQN's facilities to reflow the data in an array, giving it a different shape. Its monadic form, Deshape, simply removes all shape information, returning a list of all the elements from the array in reading order. With a left argument, `⥊` is called Reshape and is a more versatile tool for rearranging the data in an array into the desired shape. +The glyph `⥊` indicates BQN's facilities to reflow the data in an array, giving it a different shape. Its monadic form, Deshape, simply removes all shape information, returning a list of all the elements from the array in index order. With a left argument, `⥊` is called Reshape and is a more versatile tool for rearranging the data in an array into the desired shape. -Because of its dependence on the reading order of an array, Reshape is less fundamental than other array operations. Using Reshape in the central computations of a program can be a sign of imperfect usage of arrays. For example, it may be useful to use Reshape to create a constant array or repeat a sequence of values several times, but the same task might also be accomplished more simply with [Table](map.md#table) `⌜`, or by taking advantage of [leading axis agreement](leading.md#leading-axis-agreement) in arithmetic primitives. +Because of its dependence on the index order of an array, Reshape is less fundamental than other array operations. Using Reshape in the central computations of a program can be a sign of imperfect usage of arrays. For example, it may be useful to use Reshape to create a constant array or repeat a sequence of values several times, but the same task might also be accomplished more simply with [Table](map.md#table) `⌜`, or by taking advantage of [leading axis agreement](leading.md#leading-axis-agreement) in arithmetic primitives. ## Deshape @@ -48,15 +48,15 @@ The result of Deshape is a list containing the same elements as the argument. ⥊ a -The elements are ordered in reading order—left to right, then top to bottom. This means that leading axes "matter more" for ordering: if one element comes earlier in the first axis but later in the second than some other element, it will come first in the result. In another view, elements are ordered according to their [indices](indices.md). In other words, deshaping the array of indices for an array will always give a [sorted](order.md) array. +The elements are ordered in reading order—left to right, then top to bottom. This means that leading axes "matter more" for ordering: if one element comes earlier in the first axis but later in the second than some other element, it will come first in the result. In another view, elements are ordered according to their [indices](indices.md), leading to the name *index order* for this ordering. To be precise, deshaping the array of indices for an array always gives a [sorted](order.md) array. ↕≢a ⍋ ⥊ ↕≢a -This ordering is also known as *row-major* order. +This ordering is also known as *row-major* order in computing. -Deshape turns a unit argument into a single-element list, automatically [enclosing](enclose.md) it if it's an atom. However, if you know `𝕩` is a unit, a more principled way to turn it into a list is to apply [Solo](couple.md) (`≍`), which adds a length-1 axis before any other axes. If you ever add axes to the data format, Solo is more likely to continue working after this transition, unless there's a reason the result should always be a list. +Deshape turns a unit argument into a single-element list, automatically enclosing it if it's an atom. However, if you know `𝕩` is a unit, a more principled way to turn it into a list is to apply [Solo](couple.md) (`≍`), which adds a length-1 axis before any other axes. If you ever add axes to the data format, Solo is more likely to continue working after this transition, unless there's a reason the result should always be a list. ⥊ 2 ≍ 2 @@ -77,7 +77,7 @@ If the number of elements implied by the given shape—that is, `×´𝕨`—is (⥊a) ≡ ⥊ 6‿2⥊a -One common use is to generate an array with a specified shape that counts up from 0 in reading order, a reshaped [Range](range.md). The idiomatic phrase to do this is `⥊⟜(↕×´)`, since it doesn't require writing the shape and its product separately. +One use is to generate an array with a specified shape that counts up from 0 in index order, a reshaped [Range](range.md). The idiomatic phrase to do this is `⥊⟜(↕×´)`, since it doesn't require writing the shape and its product separately. 2‿7 ⥊ ↕14 ⥊⟜(↕×´) 2‿7 @@ -106,12 +106,12 @@ What if you want to reshape an array into, say, rows of length 2, but don't want ∘‿2 ⥊ "aAeEiIoOuU" -Above, the length given is `∘`, a special value that indicates that a length that fits the argument should be computed. In fact, Reshape has four different special values that can be used. Every one works the same for a case like the one above, where the rest of the shape divides the argument length evenly. They differ in how they handle uneven cases, where the required length would fall between two whole numbers. +Above, the length given is `∘`, a special value that indicates that a length fitting the argument should be computed. Reshape has four such special values that can be used. Every one works the same for a case like the one above, where the rest of the shape divides the argument length evenly. They differ in how they handle uneven cases, where the required length falls between two whole numbers. - `∘` says the length must be an exact fit, and gives an error in such a case. - `⌊` rounds the length down, so that some elements are discarded. - `⌽` rounds the length up, repeating elements to make up the difference. -- `↑` rounds the length up, but uses the argument's fill for the needed extra elements. +- `↑` rounds the length up, but uses the argument's [fill](fill.md) for the needed extra elements. These values are just BQN primitives of course. They're not called by Reshape or anything like that; the primitives are just chosen to suggest the corresponding functionality. @@ -125,7 +125,7 @@ Here's an example of the four cases. If we try to turn five elements into two ro 2‿↑ ⥊ "abcde" -A computed length can be useful to input an array without using nested notation: for example, if you have a table with rows of three elements, you might write it as one long list, using `∘‿3⥊⟨…⟩` to get it back to the appropriate shape. `∘` is definitely the value to use here, as it will check that you haven't missed an element or something like that. +A computed length can be useful to input an array without using nested [notation](arrayrepr.md#brackets): for example, if you have a table with rows of three elements, you might write it as one long list, using `∘‿3⥊⟨…⟩` to get it back to the appropriate shape. `∘` is definitely the value to use here, as it will check that you haven't missed or added an element. Computed Reshape might also be used in actual data processing: for example, to sum a list in groups of four, you might first reshape it using `↑‿4` for the shape, then average the rows. Here the code `↑` is useful because added fill elements of `0` won't change the sum, so that if the last group doesn't have four elements (`9‿7` below), it will still be summed correctly. diff --git a/doc/reverse.md b/doc/reverse.md index f2a20281..117daa23 100644 --- a/doc/reverse.md +++ b/doc/reverse.md @@ -2,13 +2,13 @@ # Reverse and Rotate -The symbol `⌽` indicates two different array transformations: with no left argument, it reverses the major cells of the array, but with a left argument, it rotates or cycles them around. These two possibilities, first put together in very early versions of APL, can't be considered restrictions or different views of some unifying function, but there are connections between them. Each returns an array with the same [shape](shape.md) and all the same elements as `𝕩`, possibly in a different arrangement. And elements that start out next to each other in `𝕩` generally stay next to each other—always, if we consider an element on one edge to be next to the one opposite to it. One might think of them as [isometries](https://en.wikipedia.org/wiki/Isometry) preserving a discrete subgroup of the torus, if one were inclined to think such things. On major cells, the two functions decompose the [dihedral group](https://en.wikipedia.org/wiki/Dihedral_group) okay I'll stop. +The symbol `⌽` indicates two different array transformations: with no left argument, it reverses the [major cells](array.md#cells) of the array, but with a left argument, it rotates or cycles them around. These two possibilities, first put together in very early versions of APL, can't be considered restrictions or different views of some unifying function, but there are connections between them. Each returns an array with the same [shape](shape.md) and all the same elements as `𝕩`, possibly in a different arrangement. And elements that start out next to each other in `𝕩` generally stay next to each other—always, if we consider an element on one edge to be next to the one opposite to it. One might think of them as [isometries](https://en.wikipedia.org/wiki/Isometry) preserving a discrete subgroup of the torus, if one were inclined to think such things. On major cells, the two functions decompose the [dihedral group](https://en.wikipedia.org/wiki/Dihedral_group) okay I'll stop. -Many uses of Rotate in APL are better handled by [shift](shift.md) functions in BQN. If there's no reason to treat the data as cyclic or periodic, it's best to avoid Rotate. +If there's no reason the data should be seen as cyclic or periodic, it's best to avoid Rotate: [shift](shift.md) functions are probably more appropriate. ## Reverse -There's not too much to say about Reverse. It puts the elements of a list the other way around, or more generally the major cells of an array. +Reverse doesn't make things complicated. It puts the elements of a list the other way around, or more generally the major cells of an array. ⌽ "abcdefg" @@ -18,7 +18,7 @@ There's not too much to say about Reverse. It puts the elements of a list the ot You can't reverse an atom or rank-0 array because it has no axes to reverse along, or it could be said no ordering to reverse. -To reverse along an axis other than the first, use Cells (`˘`) or Rank (`⎉`). +To reverse along an axis other than the first, use [Cells](rank.md#cells) (`˘`) or [Rank](rank.md#rank) (`⎉`). ⌽˘ >"ab"‿"cd"‿"ef" @@ -28,7 +28,7 @@ Reverse is useful for [folding](fold.md) left to right instead of right to left ⋈˜´ ⌽ "abcd" # Left to right -Reverse is its own [inverse](undo.md) `⌽⁼`. As a result, `𝔽⌾⌽` reverses the argument, applies `𝔽`, and reverses again. It's a particularly useful pattern with [Scan](scan.md), as it allows scanning from the end rather than the beginning of the array. For example, `` ∨` `` on a list of booleans changes all bits after the first `1` to `1`, but `` ∨`⌾⌽ `` does this to all bits before the last `1`. +Reverse is its own [inverse](undo.md) `⌽⁼`. So with [Under](under.md), `𝔽⌾⌽` reverses the argument, applies `𝔽`, and reverses again. It's a particularly useful pattern with [Scan](scan.md), as it allows scanning from the end rather than the beginning of the array. For example, `` ∨` `` on a list of booleans changes all bits after the first `1` to `1`, but `` ∨`⌾⌽ `` does this to all bits before the last `1`. ∨` 0‿0‿1‿0‿0‿1‿0 @@ -56,13 +56,13 @@ To rotate the other way, use a negative left argument (so `-⊸⌽` is a simple ### Multiple axes -The easiest way to rotate a later array axis is usually to use the Cells (`˘`) or Rank (`⎉`) modifier. +The easiest way to rotate along a later array axis is usually to use the [Cells](rank.md#cells) (`˘`) or [Rank](rank.md#rank) (`⎉`) modifier. ⊢ tab ← 3‿4⥊"abcdABCD0123" 1 ⌽˘ tab # Rotate the second axis -Rotate also allows `𝕨` to be a list (or unit array) of integers, in which case they're matched with [leading axes](leading.md) of `𝕩`. This means the length of `𝕨` can't be larger than the rank of `𝕩`, or there wouldn't be enough axes to match. This rule also explains why `𝕩` has to have rank one or more when `𝕨` is an atom, because `𝕨` is treated as the one-element list `⥊𝕨` in that case. +Rotate also allows `𝕨` to be a list (or unit array) of integers, in which case they're matched with [leading axes](leading.md) of `𝕩`. This means the length of `𝕨` can't be larger than the rank of `𝕩`, or there wouldn't be enough axes to match. This rule that `𝕩` has to have rank one or more when `𝕨` is an atom is a special case, because then `𝕨` is treated as the one-element list `⥊𝕨`. 3‿4‿2 ⌽ "just a list" @@ -70,6 +70,6 @@ The expression below rotates the first (vertical) axis of `tab` by one element, 1‿2 ⌽ tab -The vertical and horizontal rotations are independent, and could also be done with two `⌽`s and a `˘`. The multi-axis form is more convenient, and can potentially be evaluated faster that multiple separate rotations in the cases where it shows up. +The vertical and horizontal rotations are independent, and could also be done with two `⌽`s and a `˘`. The multi-axis form is more convenient, and can potentially be evaluated faster than multiple separate rotations in the cases where it shows up. 1 ⌽ 2 ⌽˘ tab -- cgit v1.2.3