diff options
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | doc/couple.md | 2 | ||||
| -rw-r--r-- | doc/depth.md | 8 | ||||
| -rw-r--r-- | doc/group.md | 2 | ||||
| -rw-r--r-- | doc/indices.md | 8 | ||||
| -rw-r--r-- | doc/join.md | 2 | ||||
| -rw-r--r-- | doc/leading.md | 2 | ||||
| -rw-r--r-- | doc/transpose.md | 6 | ||||
| -rw-r--r-- | docs/doc/couple.html | 2 | ||||
| -rw-r--r-- | docs/doc/depth.html | 8 | ||||
| -rw-r--r-- | docs/doc/group.html | 2 | ||||
| -rw-r--r-- | docs/doc/indices.html | 8 | ||||
| -rw-r--r-- | docs/doc/join.html | 2 | ||||
| -rw-r--r-- | docs/doc/leading.html | 2 | ||||
| -rw-r--r-- | docs/doc/transpose.html | 6 | ||||
| -rw-r--r-- | docs/index.html | 2 | ||||
| -rw-r--r-- | docs/spec/types.html | 2 | ||||
| -rw-r--r-- | spec/types.md | 2 |
18 files changed, 34 insertions, 34 deletions
@@ -18,7 +18,7 @@ It incorporates concepts developed over years of APL practice: * Lightweight [**anonymous functions**](doc/block.md) (like [dfns](https://aplwiki.com/wiki/Dfn)). But BQN is redesigned from the ground up, with brand new ideas to make these paradigms easier to use and less likely to fail. -* The [**based array model**](doc/based.md) makes non-arrays a fundamental part of the language, and removes the surprise of floating arrays and the hassle of explicit boxes. New **array notation** eliminates the gotchas of [stranding](https://aplwiki.com/wiki/Strand_notation). +* The [**based array model**](doc/based.md) makes non-arrays (called atoms) a fundamental part of the language, and removes the surprise of floating arrays and the hassle of explicit boxes. New **array notation** eliminates the gotchas of [stranding](https://aplwiki.com/wiki/Strand_notation). * A [**context-free grammar**](doc/context.md) where a value's syntactic role is determined by its spelling makes it easier for machines and humans to understand code. * Oh, and it naturally leads to [**first-class functions**](doc/functional.md), a feature often missed in APL. * The **new symbols** for built-in functionality allow the syntactic role of a primitive to be distinguished at a glance, and aim to be more consistent and intuitive. diff --git a/doc/couple.md b/doc/couple.md index 3b11b2b3..3cc1ac8f 100644 --- a/doc/couple.md +++ b/doc/couple.md @@ -49,6 +49,6 @@ The pair function, which creates a list from its arguments, can be written `Pair ## 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 non-arrays as well as array scalars, 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 `>{β¨π©β©;β¨π¨,π©β©}`. 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 array scalars, 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π©)`. diff --git a/doc/depth.md b/doc/depth.md index a1c900f3..13674e02 100644 --- a/doc/depth.md +++ b/doc/depth.md @@ -2,7 +2,7 @@ # Depth -The depth of an array is the greatest level of array nesting it attains, or, put another way, the greatest number of times you can pick an element starting from the original array before reaching a non-array. The monadic function Depth (`β‘`) returns the depth of its argument, while the 2-modifier Depth (`β`) can control the way its left operand is applied based on the depth of its arguments. Several primitive functions also use the depth of the left argument to decide whether it applies to a single axis of the right argument or to several axes. +The depth of an array is the greatest level of array nesting it attains, or, put another way, the greatest number of times you can pick an element starting from the original array before reaching an atom. The monadic function Depth (`β‘`) returns the depth of its argument, while the 2-modifier Depth (`β`) can control the way its left operand is applied based on the depth of its arguments. Several primitive functions also use the depth of the left argument to decide whether it applies to a single axis of the right argument or to several axes. ## The Depth function @@ -22,7 +22,7 @@ Also unlike rank, Depth *does* care about the elements of its argument: in fact, β‘ β¨2,<3,4,5β© β‘ β¨2,<3,4,<<<5β© -As the above expressions suggest, the depth of an array is the maximum of its elements' depths, plus one. The base case, a non-array (including a function or modifier), has depth 0. +As the above expressions suggest, the depth of an array is the maximum of its elements' depths, plus one. The base case, an atom (including a function or modifier), has depth 0. β‘'c' Fβ+ββ‘f @@ -56,7 +56,7 @@ In these cases the flexibility seems trivial because the left argument has depth β¨3βΏ2,1βΏ4βΏ1β© β β6βΏ7 -This means the left argument is homogeneous of depth 2. What should an argument of depth 1, or an argument that contains non-arrays, do? One option is to continue to require the left argument to be a list, and convert any non-array argument into an array by enclosing it: +This means the left argument is homogeneous of depth 2. What should an argument of depth 1, that is, an array of atoms, do? One option is to continue to require the left argument to be a list, and convert any atom argument into an array by enclosing it: β¨3βΏ2,1β© <β(0=β‘)Β¨βΈβ β6βΏ7 @@ -86,7 +86,7 @@ Depth `Β―1` is equivalent to Each, and reverses the larger vectors, while depth β½βΒ―1 n β½βΒ―2 n -While a negative depth tells how many levels to go down, a non-negative depth gives the maximum depth of the argument before applying the left operand. On a depth-3 array like above, depth `2` is equivalent to `Β―1` and depth `1` is equivalent to `Β―2`. A depth of `0` means to loop until non-arrays are reached, that is, apply [pervasively](https://aplwiki.com/wiki/Pervasion), like a scalar function. +While a negative depth tells how many levels to go down, a non-negative depth gives the maximum depth of the argument before applying the left operand. On a depth-3 array like above, depth `2` is equivalent to `Β―1` and depth `1` is equivalent to `Β―2`. A depth of `0` means to descend all the way to the level of atoms, that is, apply [pervasively](https://aplwiki.com/wiki/Pervasion), like a scalar function. β¨'a',"bc"β© ββ0 β¨2βΏ3,4β© diff --git a/doc/group.md b/doc/group.md index 17e05850..81e16f94 100644 --- a/doc/group.md +++ b/doc/group.md @@ -6,7 +6,7 @@ BQN replaces the [Key](https://aplwiki.com/wiki/Key) operator from J or Dyalog A ## Definition -Group operates on a numeric list of indices and an array, treated as a list of its major cells or "values", to produce a list of groups, each of which is a selection from those cells. The two arrays have the same length, and each value cell is paired with the index at the same position. That index indicates the result group the cell should go into, with an "index" of Β―1 indicating that it should be dropped and not appear in the result. +Group operates on a list of atomic-number indices and an array, treated as a list of its major cells or "values", to produce a list of groups, each of which is a selection from those cells. The two arrays have the same length, and each value cell is paired with the index at the same position. That index indicates the result group the cell should go into, with an "index" of Β―1 indicating that it should be dropped and not appear in the result. 0βΏ1βΏ2βΏ0βΏ1 β "abcde" # Corresponding indices and values 0βΏ1βΏ2βΏ0βΏ1 β "abcde" # Values grouped by index diff --git a/doc/indices.md b/doc/indices.md index a210f710..b98f3c33 100644 --- a/doc/indices.md +++ b/doc/indices.md @@ -2,7 +2,7 @@ # Indices -One-dimensional arrays such as K lists or Python arrays have only one kind of index, a single number that refers to an element. For multidimensional arrays using the [leading axis theory](leading.md), there are several types of indexing that can be useful. Historically, nested APL designs have equivocated between these, which I believe can lead to subtle errors when programming. BQN focuses on single-number (depth 0 or atomic) indices, which can refer to list elements or array major cells (or more generally indexing along any particular axis). When using atomic indices to select elements, the indexed array has to be a list. In contrast, elements of any array can be indicated by list indices, whose length is equal to the array's rank. Only two BQN primitives use these list indices: Range (`β`), which returns an array of them if given a list argument, and Pick (`β`), where the depth-1 components of an array left argument are list indices. +One-dimensional arrays such as K lists or Python arrays have only one kind of index, a single number that refers to an element. For multidimensional arrays using the [leading axis theory](leading.md), there are several types of indexing that can be useful. Historically, nested APL designs have equivocated between these, which I believe can lead to subtle errors when programming. BQN focuses on single-number (atomic) indices, which can refer to list elements or array major cells (or more generally indexing along any particular axis). When using atomic indices to select elements, the indexed array has to be a list. In contrast, elements of any array can be indicated by list indices with length equal to that array's rank. Only two BQN primitives use these list indices: Range (`β`), which returns an array of them if given a list argument, and Pick (`β`), where the depth-1 components of an array left argument are list indices. The following functions take or return indices. Except where marked, the indices are in the result; this is by far the most common type of index use. `β` is given two rows as it falls into both cases. Note that in the result case, there is usually no possibility for the programmer to select the format of indices. Instead, the language should be carefully designed to make sure that the kind of index returned is as useful as possible. @@ -26,9 +26,9 @@ Dyadic Transpose (`β`) uses indices into the right argument axes in its left a In general, the index of an element of an array is a list whose length matches the array rank. It is also possible to use a number for an index into a list, as the list index is a singleton, but this must be kept consistent with the rest of the language. NARS-family APLs make the Index Generator (`β` in BQN) return a numeric list when the argument has length 1 but a nested array otherwise. This means that the depth of the result depends on the shape of the argument, inverting the typical hierarchy. BQN shouldn't have such an inconsistency. -Functions `β`, `/`, `β`, and `β` naturally deal with element indices. Each of these can be defined to use list indices. However, this usually rules out the possibility of using scalar indices, which makes these functions harder to use both with generic array manipulation and with the major cell indices discussed in the next section. For this reason BQN restricts `β` and monadic `/` to use depth-0 indices, which comes with the requirement that the arguments to monadic `/` and `β`, and the result of monadic `β`, must be lists. For dyadic `β` the depth-1 elements of the left argument are lists of indices along axes of the result; see [the documentation](group.md#multidimensional-grouping). The restriction that comes from using single-number indices is that all axes must be treated independently, so that for example it isn't possible to group elements along diagonals without preprocessing. However, this restriction also prevents Group from having to use an ordering on list indices. +Functions `β`, `/`, `β`, and `β` naturally deal with element indices. Each of these can be defined to use list indices. However, this usually rules out the possibility of using scalar indices, which makes these functions harder to use both with generic array manipulation and with the major cell indices discussed in the next section. For this reason BQN restricts `β` and monadic `/` to use atomic indices, which comes with the requirement that the arguments to monadic `/` and `β`, and the result of monadic `β`, must be lists. For dyadic `β` the depth-1 elements of the left argument are lists of indices along axes of the result; see [the documentation](group.md#multidimensional-grouping). The restriction that comes from using single-number indices is that all axes must be treated independently, so that for example it isn't possible to group elements along diagonals without preprocessing. However, this restriction also keeps Group from having to use an ordering on list indices. -Unlike `/` and `β`, `β` and `β` do use list element indices. For `β` this is because the output format can be controlled by the argument format: if passed a single number, the result uses single-number indices (so it's a numeric list); if passed a list, it uses list indices and the result has depth 2 (the result depth is always one greater than the argument depth). For `β`, list indices are chosen because `β` handles scalar indices well already. When selecting multiple elements from a list, they would typically have to be placed in an array, which is equivalent to `β` with a numeric list left argument. A single scalar index to `β` is converted to a list, so it can be used to select a single element if only one is wanted. To select multiple elements, `β` uses each depth-1 array in the left argument as an index and replaces it with that element from the right argument. Because this uses elements as elements (not cells), it is impossible to have conformability errors where elements do not fit together. Ill-formed index errors are of course still possible, and the requirements on indices are quite strict. They must exactly match the structure of the right argument's shape, with no scalars or higher-rank arrays allowed. Single numbers also cannot be used in this context, as it would create ambiguity: is a one-element list an index, or does it contain an index? +Unlike `/` and `β`, `β` and `β` do use list element indices. For `β` this is because the output format can be controlled by the argument format: if passed a single number, the result uses atomic indices (so it's a numeric list); if passed a list, it uses list indices and the result has depth 2 (the result depth is always one greater than the argument depth). For `β`, list indices are chosen because `β` handles scalar indices well already. When selecting multiple elements from a list, they would typically have to be placed in an array, which is equivalent to `β` with a numeric list left argument. An atomic left argument to `β` is converted to a list, so it can be used to select a single element if only one is wanted. To select multiple elements, `β` uses each depth-1 array in the left argument as an index and replaces it with that element from the right argument. Because this uses elements as elements (not cells), it is impossible to have conformability errors where elements do not fit together. Ill-formed index errors are of course still possible, and the requirements on indices are quite strict. They must exactly match the structure of the right argument's shape, with no scalars or higher-rank arrays allowed. Atoms also cannot be used in this context, as it would create ambiguity: is a one-element list an index, or does it contain an index? # Major cell indices @@ -50,6 +50,6 @@ To match this format, Range (`β`) could be changed to return a flat array when The most interesting feature would be that `β` could still allow a nested left argument. In this case each element of the left argument would be an array with row indices as before. However, each row can now index along multiple axes, allowing some adjacent axes to be dependent while others remain independent. This nicely unifies scatter-point and per-axis selection, and allows a mix of the two. However, it doesn't allow total freedom, as non-adjacent axes can't be combined except by also mixing in all axes in between. -Group (`β`) could accept the same index format for its index argument. Each depth-1 array in the left argument would correspond to multiple axes in the outer result array, but only a single axis in the argument and inner arrays. Because the ravel ordering of indices must be used to order cells of inner arrays, this modification is not quite as clean as the change to Select. It's also not so clearly useful, as the same results can be obtained by using numeric indices and reshaping the result. +Group (`β`) could accept the same index format for its index argument. Each depth-1 array in the left argument would correspond to multiple axes in the outer result array, but only a single axis in the argument and inner arrays. Because the ravel ordering of indices must be used to order cells of inner arrays, this modification is not quite as clean as the change to Select. It's also not so clearly useful, as the same results can be obtained by using atomic indices and reshaping the result. Overall it seems to me that the main use of cell indices of the type discussed here is for the Select primitive, and the other cases are somewhat contrived an awkward. So I've chosen not to support it in BQN at all. diff --git a/doc/join.md b/doc/join.md index 05171e24..b1041c83 100644 --- a/doc/join.md +++ b/doc/join.md @@ -14,7 +14,7 @@ To join with a separator in between, we might prepend the separator to each stri Join requires each element of its argument to be an array, and their ranks to match exactly. No rank extension is performed. - βΎ"abc"βΏ'd'βΏ"ef" # Includes a non-array + βΎ"abc"βΏ'd'βΏ"ef" # Includes an atom RANK ERROR βΎ"abc"βΏ(<'d')βΏ"ef" # Includes a scalar RANK ERROR diff --git a/doc/leading.md b/doc/leading.md index 35d72295..d11773d2 100644 --- a/doc/leading.md +++ b/doc/leading.md @@ -8,7 +8,7 @@ Several primitive functions manipulate the right argument, or sometimes both arg ### Manipulating cells -Most non-scalar monadic functions work only on the first axis of the argumentβthat is, they treat it as a list of its major cells. The function Length (`β `) counts these major cells, while Prefixes (`β`), Suffixes (`β`), Reverse (`β½`), and First Cell (`β`) move them around. The Insert (`Λ`) and Scan (`` ` ``) modifiers also yield functions that work along the first axis; in contrast, Reduce (`Β΄`) requires its argument to be a list, as it works on elements. +Most non-arithmetic monadic functions work only on the first axis of the argumentβthat is, they treat it as a list of its major cells. The function Length (`β `) counts these major cells, while Prefixes (`β`), Suffixes (`β`), Reverse (`β½`), and First Cell (`β`) move them around. The Insert (`Λ`) and Scan (`` ` ``) modifiers also yield functions that work along the first axis; in contrast, Reduce (`Β΄`) requires its argument to be a list, as it works on elements. β’ a β 3βΏ2 β₯ "abcdef" # An array with three major cells β a # Get the first major cell diff --git a/doc/transpose.md b/doc/transpose.md index f7034e83..ae72d889 100644 --- a/doc/transpose.md +++ b/doc/transpose.md @@ -74,8 +74,8 @@ Finally, it's worth noting that, as monadic Transpose moves the first axis to th Here we define the two valences of Transpose more precisely. -A non-array right argument to Transpose is always enclosed to get a scalar array before doing anything else. +Monadic transpose is identical to `(=-1Λ)βΈβ`, except that for scalar arguments (including atoms) it returns the array unchanged rather than giving an error. -Monadic transpose is identical to `(=-1Λ)βΈβ`, except that for scalar arguments it returns the array unchanged rather than giving an error. +An atom right argument to dyadic Transpose is always enclosed to get a scalar array before doing anything else. -In Dyadic transpose, the left argument is a number or numeric array of rank 1 or less, and `π¨β€ββ β’π©`. Define the result rank `rβ(=π©)-+´¬βπ¨` to be the argument rank minus the number of duplicate entries in the left argument. We require `β§Β΄π¨<r`. Bring `π¨` to full length by appending the missing indices: `π¨βΎβ©π¨(Β¬ββΛ/β’)βr`. Now the result shape is defined to be `β´¨π¨ββ’π©`. Element `iβz` of the result `z` is element `(π¨βi)βπ©` of the argument. +In dyadic Transpose, the left argument is a number or numeric array of rank 1 or less, and `π¨β€ββ β’π©`. Define the result rank `rβ(=π©)-+´¬βπ¨` to be the argument rank minus the number of duplicate entries in the left argument. We require `β§Β΄π¨<r`. Bring `π¨` to full length by appending the missing indices: `π¨βΎβ©π¨(Β¬ββΛ/β’)βr`. Now the result shape is defined to be `β´¨π¨ββ’π©`. Element `iβz` of the result `z` is element `(π¨βi)βπ©` of the argument. diff --git a/docs/doc/couple.html b/docs/doc/couple.html index 06f200c5..8a94fe4e 100644 --- a/docs/doc/couple.html +++ b/docs/doc/couple.html @@ -83,6 +83,6 @@ </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>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 atoms 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/depth.html b/docs/doc/depth.html index 409e3799..7ae6b2fd 100644 --- a/docs/doc/depth.html +++ b/docs/doc/depth.html @@ -5,7 +5,7 @@ </head> <div class="nav"><a href="https://github.com/mlochbaum/BQN">BQN</a></div> <h1 id="depth">Depth</h1> -<p>The depth of an array is the greatest level of array nesting it attains, or, put another way, the greatest number of times you can pick an element starting from the original array before reaching a non-array. The monadic function Depth (<code><span class='Function'>β‘</span></code>) returns the depth of its argument, while the 2-modifier Depth (<code><span class='Modifier2'>β</span></code>) can control the way its left operand is applied based on the depth of its arguments. Several primitive functions also use the depth of the left argument to decide whether it applies to a single axis of the right argument or to several axes.</p> +<p>The depth of an array is the greatest level of array nesting it attains, or, put another way, the greatest number of times you can pick an element starting from the original array before reaching an atom. The monadic function Depth (<code><span class='Function'>β‘</span></code>) returns the depth of its argument, while the 2-modifier Depth (<code><span class='Modifier2'>β</span></code>) can control the way its left operand is applied based on the depth of its arguments. Several primitive functions also use the depth of the left argument to decide whether it applies to a single axis of the right argument or to several axes.</p> <h2 id="the-depth-function">The Depth function</h2> <p>To find the depth of an array, use Depth (<code><span class='Function'>β‘</span></code>). For example, the depth of a list of numbers or characters is 1:</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4omhIDLigL8z4oC/NAriiaEgImEgc3RyaW5nIGlzIGEgbGlzdCBvZiBjaGFyYWN0ZXJzIg==&run">βοΈ</a><pre> <span class='Function'>β‘</span> <span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>4</span> @@ -27,7 +27,7 @@ <span class='Function'>β‘</span> <span class='Bracket'>β¨</span><span class='Number'>2</span><span class='Separator'>,</span><span class='Function'><</span><span class='Number'>3</span><span class='Separator'>,</span><span class='Number'>4</span><span class='Separator'>,</span><span class='Function'><<<</span><span class='Number'>5</span><span class='Bracket'>β©</span> 4 </pre> -<p>As the above expressions suggest, the depth of an array is the maximum of its elements' depths, plus one. The base case, a non-array (including a function or modifier), has depth 0.</p> +<p>As the above expressions suggest, the depth of an array is the maximum of its elements' depths, plus one. The base case, an atom (including a function or modifier), has depth 0.</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4omhJ2MnCkbihpAr4ouE4omhZgriiaHin6gnYycsZiwy4p+pCuKJoeKfqDUs4p+oJ2MnLGYsMuKfqeKfqQ==&run">βοΈ</a><pre> <span class='Function'>β‘</span><span class='String'>'c'</span> 0 <span class='Function'>F</span><span class='Gets'>β</span><span class='Function'>+</span><span class='Separator'>β</span><span class='Function'>β‘</span><span class='Value'>f</span> @@ -79,7 +79,7 @@ β¨ 2 1 β© β¨ 2 4 β© β¨ 2 1 β© β </pre> -<p>This means the left argument is homogeneous of depth 2. What should an argument of depth 1, or an argument that contains non-arrays, do? One option is to continue to require the left argument to be a list, and convert any non-array argument into an array by enclosing it:</p> +<p>This means the left argument is homogeneous of depth 2. What should an argument of depth 1, that is, an array of atoms, do? One option is to continue to require the left argument to be a list, and convert any atom argument into an array by enclosing it:</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4p+oM+KAvzIsMeKfqSA84o2fKDA94omhKcKo4oq44oqPIOKGlTbigL83&run">βοΈ</a><pre> <span class='Bracket'>β¨</span><span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>2</span><span class='Separator'>,</span><span class='Number'>1</span><span class='Bracket'>β©</span> <span class='Function'><</span><span class='Modifier2'>β</span><span class='Paren'>(</span><span class='Number'>0</span><span class='Function'>=β‘</span><span class='Paren'>)</span><span class='Modifier'>Β¨</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span> <span class='Function'>β</span><span class='Number'>6</span><span class='Ligature'>βΏ</span><span class='Number'>7</span> β¨ β¨ 3 1 β© β¨ 2 1 β© β© </pre> @@ -129,7 +129,7 @@ β¨ β¨ 37 36 β© β¨ 39 38 β© β© β¨ β¨ 41 40 β© β¨ 43 42 β© β© β¨ β¨ 45 44 β© β¨ 47 46 β© β© β </pre> -<p>While a negative depth tells how many levels to go down, a non-negative depth gives the maximum depth of the argument before applying the left operand. On a depth-3 array like above, depth <code><span class='Number'>2</span></code> is equivalent to <code><span class='Number'>Β―1</span></code> and depth <code><span class='Number'>1</span></code> is equivalent to <code><span class='Number'>Β―2</span></code>. A depth of <code><span class='Number'>0</span></code> means to loop until non-arrays are reached, that is, apply <a href="https://aplwiki.com/wiki/Pervasion">pervasively</a>, like a scalar function.</p> +<p>While a negative depth tells how many levels to go down, a non-negative depth gives the maximum depth of the argument before applying the left operand. On a depth-3 array like above, depth <code><span class='Number'>2</span></code> is equivalent to <code><span class='Number'>Β―1</span></code> and depth <code><span class='Number'>1</span></code> is equivalent to <code><span class='Number'>Β―2</span></code>. A depth of <code><span class='Number'>0</span></code> means to descend all the way to the level of atoms, that is, apply <a href="https://aplwiki.com/wiki/Pervasion">pervasively</a>, like a scalar function.</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4p+oJ2EnLCJiYyLin6kg4omN4pqHMCDin6gy4oC/Myw04p+p&run">βοΈ</a><pre> <span class='Bracket'>β¨</span><span class='String'>'a'</span><span class='Separator'>,</span><span class='String'>"bc"</span><span class='Bracket'>β©</span> <span class='Function'>β</span><span class='Modifier2'>β</span><span class='Number'>0</span> <span class='Bracket'>β¨</span><span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>3</span><span class='Separator'>,</span><span class='Number'>4</span><span class='Bracket'>β©</span> ββ Β· β¨ β¨ 'a' 2 β© β¨ 'a' 3 β© β© β¨ β¨ 'b' 4 β© β¨ 'c' 4 β© β© diff --git a/docs/doc/group.html b/docs/doc/group.html index 57ab5562..25bc3748 100644 --- a/docs/doc/group.html +++ b/docs/doc/group.html @@ -7,7 +7,7 @@ <h1 id="group">Group</h1> <p>BQN replaces the <a href="https://aplwiki.com/wiki/Key">Key</a> operator from J or Dyalog APL, and <a href="https://aplwiki.com/wiki/Partition_representations">many forms of partitioning</a>, with a single (ambivalent) Group function <code><span class='Function'>β</span></code>. This function is somewhat related to the K function <code><span class='Function'>=</span></code> of the same name, but results in an array rather than a dictionary.</p> <h2 id="definition">Definition</h2> -<p>Group operates on a numeric list of indices and an array, treated as a list of its major cells or "values", to produce a list of groups, each of which is a selection from those cells. The two arrays have the same length, and each value cell is paired with the index at the same position. That index indicates the result group the cell should go into, with an "index" of Β―1 indicating that it should be dropped and not appear in the result.</p> +<p>Group operates on a list of atomic-number indices and an array, treated as a list of its major cells or "values", to produce a list of groups, each of which is a selection from those cells. The two arrays have the same length, and each value cell is paired with the index at the same position. That index indicates the result group the cell should go into, with an "index" of Β―1 indicating that it should be dropped and not appear in the result.</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=MOKAvzHigL8y4oC/MOKAvzEg4omNICJhYmNkZSIgICMgQ29ycmVzcG9uZGluZyBpbmRpY2VzIGFuZCB2YWx1ZXMKMOKAvzHigL8y4oC/MOKAvzEg4oqUICJhYmNkZSIgICMgVmFsdWVzIGdyb3VwZWQgYnkgaW5kZXg=&run">βοΈ</a><pre> <span class='Number'>0</span><span class='Ligature'>βΏ</span><span class='Number'>1</span><span class='Ligature'>βΏ</span><span class='Number'>2</span><span class='Ligature'>βΏ</span><span class='Number'>0</span><span class='Ligature'>βΏ</span><span class='Number'>1</span> <span class='Function'>β</span> <span class='String'>"abcde"</span> <span class='Comment'># Corresponding indices and values </span>ββ β΅ 0 1 2 0 1 diff --git a/docs/doc/indices.html b/docs/doc/indices.html index 31caa122..b56e7656 100644 --- a/docs/doc/indices.html +++ b/docs/doc/indices.html @@ -5,7 +5,7 @@ </head> <div class="nav"><a href="https://github.com/mlochbaum/BQN">BQN</a></div> <h1 id="indices">Indices</h1> -<p>One-dimensional arrays such as K lists or Python arrays have only one kind of index, a single number that refers to an element. For multidimensional arrays using the <a href="leading.html">leading axis theory</a>, there are several types of indexing that can be useful. Historically, nested APL designs have equivocated between these, which I believe can lead to subtle errors when programming. BQN focuses on single-number (depth 0 or atomic) indices, which can refer to list elements or array major cells (or more generally indexing along any particular axis). When using atomic indices to select elements, the indexed array has to be a list. In contrast, elements of any array can be indicated by list indices, whose length is equal to the array's rank. Only two BQN primitives use these list indices: Range (<code><span class='Function'>β</span></code>), which returns an array of them if given a list argument, and Pick (<code><span class='Function'>β</span></code>), where the depth-1 components of an array left argument are list indices.</p> +<p>One-dimensional arrays such as K lists or Python arrays have only one kind of index, a single number that refers to an element. For multidimensional arrays using the <a href="leading.html">leading axis theory</a>, there are several types of indexing that can be useful. Historically, nested APL designs have equivocated between these, which I believe can lead to subtle errors when programming. BQN focuses on single-number (atomic) indices, which can refer to list elements or array major cells (or more generally indexing along any particular axis). When using atomic indices to select elements, the indexed array has to be a list. In contrast, elements of any array can be indicated by list indices with length equal to that array's rank. Only two BQN primitives use these list indices: Range (<code><span class='Function'>β</span></code>), which returns an array of them if given a list argument, and Pick (<code><span class='Function'>β</span></code>), where the depth-1 components of an array left argument are list indices.</p> <p>The following functions take or return indices. Except where marked, the indices are in the result; this is by far the most common type of index use. <code><span class='Function'>β</span></code> is given two rows as it falls into both cases. Note that in the result case, there is usually no possibility for the programmer to select the format of indices. Instead, the language should be carefully designed to make sure that the kind of index returned is as useful as possible.</p> <table> <thead> @@ -88,8 +88,8 @@ <p>Dyadic Transpose (<code><span class='Function'>β</span></code>) uses indices into the right argument axes in its left argument, but since array shape is 1-dimensional, there is only one sensible choice for this, a single number.</p> <h1 id="element-indices">Element indices</h1> <p>In general, the index of an element of an array is a list whose length matches the array rank. It is also possible to use a number for an index into a list, as the list index is a singleton, but this must be kept consistent with the rest of the language. NARS-family APLs make the Index Generator (<code><span class='Function'>β</span></code> in BQN) return a numeric list when the argument has length 1 but a nested array otherwise. This means that the depth of the result depends on the shape of the argument, inverting the typical hierarchy. BQN shouldn't have such an inconsistency.</p> -<p>Functions <code><span class='Function'>β</span></code>, <code><span class='Function'>/</span></code>, <code><span class='Function'>β</span></code>, and <code><span class='Function'>β</span></code> naturally deal with element indices. Each of these can be defined to use list indices. However, this usually rules out the possibility of using scalar indices, which makes these functions harder to use both with generic array manipulation and with the major cell indices discussed in the next section. For this reason BQN restricts <code><span class='Function'>β</span></code> and monadic <code><span class='Function'>/</span></code> to use depth-0 indices, which comes with the requirement that the arguments to monadic <code><span class='Function'>/</span></code> and <code><span class='Function'>β</span></code>, and the result of monadic <code><span class='Function'>β</span></code>, must be lists. For dyadic <code><span class='Function'>β</span></code> the depth-1 elements of the left argument are lists of indices along axes of the result; see <a href="group.html#multidimensional-grouping">the documentation</a>. The restriction that comes from using single-number indices is that all axes must be treated independently, so that for example it isn't possible to group elements along diagonals without preprocessing. However, this restriction also prevents Group from having to use an ordering on list indices.</p> -<p>Unlike <code><span class='Function'>/</span></code> and <code><span class='Function'>β</span></code>, <code><span class='Function'>β</span></code> and <code><span class='Function'>β</span></code> do use list element indices. For <code><span class='Function'>β</span></code> this is because the output format can be controlled by the argument format: if passed a single number, the result uses single-number indices (so it's a numeric list); if passed a list, it uses list indices and the result has depth 2 (the result depth is always one greater than the argument depth). For <code><span class='Function'>β</span></code>, list indices are chosen because <code><span class='Function'>β</span></code> handles scalar indices well already. When selecting multiple elements from a list, they would typically have to be placed in an array, which is equivalent to <code><span class='Function'>β</span></code> with a numeric list left argument. A single scalar index to <code><span class='Function'>β</span></code> is converted to a list, so it can be used to select a single element if only one is wanted. To select multiple elements, <code><span class='Function'>β</span></code> uses each depth-1 array in the left argument as an index and replaces it with that element from the right argument. Because this uses elements as elements (not cells), it is impossible to have conformability errors where elements do not fit together. Ill-formed index errors are of course still possible, and the requirements on indices are quite strict. They must exactly match the structure of the right argument's shape, with no scalars or higher-rank arrays allowed. Single numbers also cannot be used in this context, as it would create ambiguity: is a one-element list an index, or does it contain an index?</p> +<p>Functions <code><span class='Function'>β</span></code>, <code><span class='Function'>/</span></code>, <code><span class='Function'>β</span></code>, and <code><span class='Function'>β</span></code> naturally deal with element indices. Each of these can be defined to use list indices. However, this usually rules out the possibility of using scalar indices, which makes these functions harder to use both with generic array manipulation and with the major cell indices discussed in the next section. For this reason BQN restricts <code><span class='Function'>β</span></code> and monadic <code><span class='Function'>/</span></code> to use atomic indices, which comes with the requirement that the arguments to monadic <code><span class='Function'>/</span></code> and <code><span class='Function'>β</span></code>, and the result of monadic <code><span class='Function'>β</span></code>, must be lists. For dyadic <code><span class='Function'>β</span></code> the depth-1 elements of the left argument are lists of indices along axes of the result; see <a href="group.html#multidimensional-grouping">the documentation</a>. The restriction that comes from using single-number indices is that all axes must be treated independently, so that for example it isn't possible to group elements along diagonals without preprocessing. However, this restriction also keeps Group from having to use an ordering on list indices.</p> +<p>Unlike <code><span class='Function'>/</span></code> and <code><span class='Function'>β</span></code>, <code><span class='Function'>β</span></code> and <code><span class='Function'>β</span></code> do use list element indices. For <code><span class='Function'>β</span></code> this is because the output format can be controlled by the argument format: if passed a single number, the result uses atomic indices (so it's a numeric list); if passed a list, it uses list indices and the result has depth 2 (the result depth is always one greater than the argument depth). For <code><span class='Function'>β</span></code>, list indices are chosen because <code><span class='Function'>β</span></code> handles scalar indices well already. When selecting multiple elements from a list, they would typically have to be placed in an array, which is equivalent to <code><span class='Function'>β</span></code> with a numeric list left argument. An atomic left argument to <code><span class='Function'>β</span></code> is converted to a list, so it can be used to select a single element if only one is wanted. To select multiple elements, <code><span class='Function'>β</span></code> uses each depth-1 array in the left argument as an index and replaces it with that element from the right argument. Because this uses elements as elements (not cells), it is impossible to have conformability errors where elements do not fit together. Ill-formed index errors are of course still possible, and the requirements on indices are quite strict. They must exactly match the structure of the right argument's shape, with no scalars or higher-rank arrays allowed. Atoms also cannot be used in this context, as it would create ambiguity: is a one-element list an index, or does it contain an index?</p> <h1 id="major-cell-indices">Major cell indices</h1> <p>One of the successes of the <a href="https://aplwiki.com/wiki/Leading_axis_theory">leading axis model</a> is to introduce a kind of index for multidimensional arrays that is easier to work with than list indices. The model introduces <a href="https://aplwiki.com/wiki/Cell">cells</a>, where a cell index is a list of any length up to the containing array's rank. General cell indices are discussed in the next section; first we introduce a special case, indices into major cells or Β―1-cells. These cells naturally form a list, so the index of a major cell is a single number. These indices can also be considered indices along the first axis, since an index along any axis is a single number.</p> <p>Ordering-based functions <code><span class='Function'>β</span></code>, <code><span class='Function'>β</span></code>, <code><span class='Function'>β</span></code>, and <code><span class='Function'>β</span></code> only really make sense with major cell indices: while it's possible to order other indices as ravel indices, this probably isn't useful from a programming standpoint. Note that <code><span class='Function'>β</span></code> only uses the ordering in an incidental way, because it's defined to return the <em>first</em> index where a right argument cell is found. A mathematician would be more interested in a "pre-image" function that returns the set of all indices where a particular value appears. However, programming usefulness and consistency with the other search functions makes searching for the first index a reasonable choice.</p> @@ -100,6 +100,6 @@ <p>Because the shape of a cell index relates to the shape of the indexed array, it makes sense not to enclose cell indices, instead treating them as rows of an index array. A definition for <code><span class='Function'>β</span></code> for depth-1 left arguments of rank at least 1 follows: replace each row of the left argument with the indexed cell of the right, yielding a result with the same depth as the right argument and shape <code><span class='Value'>π¨</span><span class='Paren'>((</span><span class='Number'>Β―1</span><span class='Function'>ββ£</span><span class='Paren'>)</span><span class='Function'>βΎ</span><span class='Paren'>(</span><span class='Number'>Β―1</span><span class='Function'>ββ£</span><span class='Paren'>)</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span><span class='Paren'>)</span><span class='Modifier2'>β</span><span class='Function'>β’</span><span class='Value'>π©</span></code>.</p> <p>To match this format, Range (<code><span class='Function'>β</span></code>) could be changed to return a flat array when given a shapeβwhat is now <code><span class='Function'>>β</span></code>. Following this pattern, Indices (<code><span class='Function'>/</span></code>) would also return a flat array, where the indices are rows: using the modified Range, <code><span class='Function'>β₯/β</span><span class='Modifier2'>β</span><span class='Function'>β’</span></code>. Here the result cannot retain the argument's array structure; it is always a rank-2 list of rows.</p> <p>The most interesting feature would be that <code><span class='Function'>β</span></code> could still allow a nested left argument. In this case each element of the left argument would be an array with row indices as before. However, each row can now index along multiple axes, allowing some adjacent axes to be dependent while others remain independent. This nicely unifies scatter-point and per-axis selection, and allows a mix of the two. However, it doesn't allow total freedom, as non-adjacent axes can't be combined except by also mixing in all axes in between.</p> -<p>Group (<code><span class='Function'>β</span></code>) could accept the same index format for its index argument. Each depth-1 array in the left argument would correspond to multiple axes in the outer result array, but only a single axis in the argument and inner arrays. Because the ravel ordering of indices must be used to order cells of inner arrays, this modification is not quite as clean as the change to Select. It's also not so clearly useful, as the same results can be obtained by using numeric indices and reshaping the result.</p> +<p>Group (<code><span class='Function'>β</span></code>) could accept the same index format for its index argument. Each depth-1 array in the left argument would correspond to multiple axes in the outer result array, but only a single axis in the argument and inner arrays. Because the ravel ordering of indices must be used to order cells of inner arrays, this modification is not quite as clean as the change to Select. It's also not so clearly useful, as the same results can be obtained by using atomic indices and reshaping the result.</p> <p>Overall it seems to me that the main use of cell indices of the type discussed here is for the Select primitive, and the other cases are somewhat contrived an awkward. So I've chosen not to support it in BQN at all.</p> diff --git a/docs/doc/join.html b/docs/doc/join.html index e465ba66..c56a47d3 100644 --- a/docs/doc/join.html +++ b/docs/doc/join.html @@ -15,7 +15,7 @@ "time to join some words" </pre> <p>Join requires each element of its argument to be an array, and their ranks to match exactly. No rank extension is performed.</p> -<pre> <span class='Function'>βΎ</span><span class='String'>"abc"</span><span class='Ligature'>βΏ</span><span class='String'>'d'</span><span class='Ligature'>βΏ</span><span class='String'>"ef"</span> <span class='Comment'># Includes a non-array +<pre> <span class='Function'>βΎ</span><span class='String'>"abc"</span><span class='Ligature'>βΏ</span><span class='String'>'d'</span><span class='Ligature'>βΏ</span><span class='String'>"ef"</span> <span class='Comment'># Includes an atom </span><span class='Function'>RANK</span> <span class='Function'>ERROR</span> <span class='Function'>βΎ</span><span class='String'>"abc"</span><span class='Ligature'>βΏ</span><span class='Paren'>(</span><span class='Function'><</span><span class='String'>'d'</span><span class='Paren'>)</span><span class='Ligature'>βΏ</span><span class='String'>"ef"</span> <span class='Comment'># Includes a scalar </span><span class='Function'>RANK</span> <span class='Function'>ERROR</span> diff --git a/docs/doc/leading.html b/docs/doc/leading.html index 9a9b522b..de9ea039 100644 --- a/docs/doc/leading.html +++ b/docs/doc/leading.html @@ -8,7 +8,7 @@ <p>Several primitive functions manipulate the right argument, or sometimes both arguments, along one or more axes. According to the <a href="https://aplwiki.com/wiki/Leading_axis_theory">leading axis model</a>, it's best to make the primitives operate on initial axes, because the Rank modifier then allows it to apply to later axes as well. Here we'll see how this pattern works in BQN.</p> <h2 id="monadic-functions">Monadic functions</h2> <h3 id="manipulating-cells">Manipulating cells</h3> -<p>Most non-scalar monadic functions work only on the first axis of the argumentβthat is, they treat it as a list of its major cells. The function Length (<code><span class='Function'>β </span></code>) counts these major cells, while Prefixes (<code><span class='Function'>β</span></code>), Suffixes (<code><span class='Function'>β</span></code>), Reverse (<code><span class='Function'>β½</span></code>), and First Cell (<code><span class='Function'>β</span></code>) move them around. The Insert (<code><span class='Modifier'>Λ</span></code>) and Scan (<code><span class='Modifier'>`</span></code>) modifiers also yield functions that work along the first axis; in contrast, Reduce (<code><span class='Modifier'>Β΄</span></code>) requires its argument to be a list, as it works on elements.</p> +<p>Most non-arithmetic monadic functions work only on the first axis of the argumentβthat is, they treat it as a list of its major cells. The function Length (<code><span class='Function'>β </span></code>) counts these major cells, while Prefixes (<code><span class='Function'>β</span></code>), Suffixes (<code><span class='Function'>β</span></code>), Reverse (<code><span class='Function'>β½</span></code>), and First Cell (<code><span class='Function'>β</span></code>) move them around. The Insert (<code><span class='Modifier'>Λ</span></code>) and Scan (<code><span class='Modifier'>`</span></code>) modifiers also yield functions that work along the first axis; in contrast, Reduce (<code><span class='Modifier'>Β΄</span></code>) requires its argument to be a list, as it works on elements.</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4oqiIGEg4oaQIDPigL8yIOKliiAiYWJjZGVmIiAgIyBBbiBhcnJheSB3aXRoIHRocmVlIG1ham9yIGNlbGxzCuKKjyBhICAgICAgICAgICAgICAgICAgICMgR2V0IHRoZSBmaXJzdCBtYWpvciBjZWxsCuKMvSBhICAgICAgICAgICAgICAgICAgICMgUmV2ZXJzZSB0aGUgY2VsbHMK4oqjYCBhICAgICAgICAgICAgICAgICAgIyBSZXBsaWNhdGUgdGhlIGZpcnN0IGNlbGw=&run">βοΈ</a><pre> <span class='Function'>β’</span> <span class='Value'>a</span> <span class='Gets'>β</span> <span class='Number'>3</span><span class='Ligature'>βΏ</span><span class='Number'>2</span> <span class='Function'>β₯</span> <span class='String'>"abcdef"</span> <span class='Comment'># An array with three major cells </span>ββ β΅"ab diff --git a/docs/doc/transpose.html b/docs/doc/transpose.html index 5f0900b8..c8b54b42 100644 --- a/docs/doc/transpose.html +++ b/docs/doc/transpose.html @@ -87,7 +87,7 @@ <p>Finally, it's worth noting that, as monadic Transpose moves the first axis to the end, it's equivalent to dyadic Transpose with a "default" left argument: <code><span class='Paren'>(</span><span class='Function'>=-</span><span class='Number'>1</span><span class='Modifier'>Λ</span><span class='Paren'>)</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span></code>.</p> <h2 id="definitions">Definitions</h2> <p>Here we define the two valences of Transpose more precisely.</p> -<p>A non-array right argument to Transpose is always enclosed to get a scalar array before doing anything else.</p> -<p>Monadic transpose is identical to <code><span class='Paren'>(</span><span class='Function'>=-</span><span class='Number'>1</span><span class='Modifier'>Λ</span><span class='Paren'>)</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span></code>, except that for scalar arguments it returns the array unchanged rather than giving an error.</p> -<p>In Dyadic transpose, the left argument is a number or numeric array of rank 1 or less, and <code><span class='Value'>π¨</span><span class='Function'>β€</span><span class='Modifier2'>β</span><span class='Function'>β β’</span><span class='Value'>π©</span></code>. Define the result rank <code><span class='Value'>r</span><span class='Gets'>β</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='Function'>Β¬β</span><span class='Value'>π¨</span></code> to be the argument rank minus the number of duplicate entries in the left argument. We require <code><span class='Function'>β§</span><span class='Modifier'>Β΄</span><span class='Value'>π¨</span><span class='Function'><</span><span class='Value'>r</span></code>. Bring <code><span class='Value'>π¨</span></code> to full length by appending the missing indices: <code><span class='Value'>π¨</span><span class='Function'>βΎ</span><span class='Gets'>β©</span><span class='Value'>π¨</span><span class='Paren'>(</span><span class='Function'>Β¬</span><span class='Modifier2'>β</span><span class='Function'>β</span><span class='Modifier'>Λ</span><span class='Function'>/β’</span><span class='Paren'>)</span><span class='Function'>β</span><span class='Value'>r</span></code>. Now the result shape is defined to be <code><span class='Function'>β</span><span class='Modifier'>´¨</span><span class='Value'>π¨</span><span class='Function'>ββ’</span><span class='Value'>π©</span></code>. Element <code><span class='Value'>i</span><span class='Function'>β</span><span class='Value'>z</span></code> of the result <code><span class='Value'>z</span></code> is element <code><span class='Paren'>(</span><span class='Value'>π¨</span><span class='Function'>β</span><span class='Value'>i</span><span class='Paren'>)</span><span class='Function'>β</span><span class='Value'>π©</span></code> of the argument.</p> +<p>Monadic transpose is identical to <code><span class='Paren'>(</span><span class='Function'>=-</span><span class='Number'>1</span><span class='Modifier'>Λ</span><span class='Paren'>)</span><span class='Modifier2'>βΈ</span><span class='Function'>β</span></code>, except that for scalar arguments (including atoms) it returns the array unchanged rather than giving an error.</p> +<p>An atom right argument to dyadic Transpose is always enclosed to get a scalar array before doing anything else.</p> +<p>In dyadic Transpose, the left argument is a number or numeric array of rank 1 or less, and <code><span class='Value'>π¨</span><span class='Function'>β€</span><span class='Modifier2'>β</span><span class='Function'>β β’</span><span class='Value'>π©</span></code>. Define the result rank <code><span class='Value'>r</span><span class='Gets'>β</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='Function'>Β¬β</span><span class='Value'>π¨</span></code> to be the argument rank minus the number of duplicate entries in the left argument. We require <code><span class='Function'>β§</span><span class='Modifier'>Β΄</span><span class='Value'>π¨</span><span class='Function'><</span><span class='Value'>r</span></code>. Bring <code><span class='Value'>π¨</span></code> to full length by appending the missing indices: <code><span class='Value'>π¨</span><span class='Function'>βΎ</span><span class='Gets'>β©</span><span class='Value'>π¨</span><span class='Paren'>(</span><span class='Function'>Β¬</span><span class='Modifier2'>β</span><span class='Function'>β</span><span class='Modifier'>Λ</span><span class='Function'>/β’</span><span class='Paren'>)</span><span class='Function'>β</span><span class='Value'>r</span></code>. Now the result shape is defined to be <code><span class='Function'>β</span><span class='Modifier'>´¨</span><span class='Value'>π¨</span><span class='Function'>ββ’</span><span class='Value'>π©</span></code>. Element <code><span class='Value'>i</span><span class='Function'>β</span><span class='Value'>z</span></code> of the result <code><span class='Value'>z</span></code> is element <code><span class='Paren'>(</span><span class='Value'>π¨</span><span class='Function'>β</span><span class='Value'>i</span><span class='Paren'>)</span><span class='Function'>β</span><span class='Value'>π©</span></code> of the argument.</p> diff --git a/docs/index.html b/docs/index.html index dcd1075e..3cee0ead 100644 --- a/docs/index.html +++ b/docs/index.html @@ -29,7 +29,7 @@ </ul> <p>But BQN is redesigned from the ground up, with brand new ideas to make these paradigms easier to use and less likely to fail.</p> <ul> -<li>The <a href="doc/based.html"><strong>based array model</strong></a> makes non-arrays a fundamental part of the language, and removes the surprise of floating arrays and the hassle of explicit boxes. New <strong>array notation</strong> eliminates the gotchas of <a href="https://aplwiki.com/wiki/Strand_notation">stranding</a>.</li> +<li>The <a href="doc/based.html"><strong>based array model</strong></a> makes non-arrays (called atoms) a fundamental part of the language, and removes the surprise of floating arrays and the hassle of explicit boxes. New <strong>array notation</strong> eliminates the gotchas of <a href="https://aplwiki.com/wiki/Strand_notation">stranding</a>.</li> <li>A <a href="doc/context.html"><strong>context-free grammar</strong></a> where a value's syntactic role is determined by its spelling makes it easier for machines and humans to understand code.</li> <li>Oh, and it naturally leads to <a href="doc/functional.html"><strong>first-class functions</strong></a>, a feature often missed in APL.</li> <li>The <strong>new symbols</strong> for built-in functionality allow the syntactic role of a primitive to be distinguished at a glance, and aim to be more consistent and intuitive.</li> diff --git a/docs/spec/types.html b/docs/spec/types.html index 3f7c38fc..13345808 100644 --- a/docs/spec/types.html +++ b/docs/spec/types.html @@ -22,5 +22,5 @@ </ul> <p>To begin the data types, a <em>character</em> is a <a href="https://en.wikipedia.org/wiki/Unicode">Unicode</a> code point, that is, its value is a non-negative integer within the ranges defined by Unicode (however, it is distinct from this number as a BQN value). Characters are ordered by this numeric value. BQN deals with code points as abstract entities and does not expose encodings such as UTF-8 or UTF-16.</p> <p>The precise type of a <em>number</em> may vary across BQN implementations or instances. A <em>real number</em> is a member of some supported subset of the <a href="https://en.wikipedia.org/wiki/Extended_real_number_line">extended real numbers</a>, that is, the real numbers and positive or negative infinity. Some system must be defined for rounding an arbitrary real number to a member of this subset, and the basic arithmetic operations add, subtract, multiply, divide, and natural exponent (base <em>e</em>) are defined by performing these operations on exact real values and rounding the result. The Power function (dyadic <code><span class='Function'>β</span></code>) is also used but need not be exactly rounded. A <em>complex number</em> is a value with two real number <em>components</em>, a <em>real part</em> and an <em>imaginary part</em>. A BQN implementation can either support real numbers only, or complex numbers.</p> -<p>An <em>array</em> is a rectangular collection of data. It is defined by a <em>shape</em>, which is a list of non-negative integer lengths, and a <em>ravel</em>, which is a list of <em>elements</em> whose length (the array's <em>bound</em>) is the product of all lengths in the shape. Arrays are defined inductively: any value (of a value or function type) can be used as an element of an array, but it is not possible for an array to contain itself as an element, or an array that contains itself, and so on.</p> +<p>An <em>array</em> is a rectangular collection of data. It is defined by a <em>shape</em>, which is a list of non-negative integer lengths, and a <em>ravel</em>, which is a list of <em>elements</em> whose length (the array's <em>bound</em>) is the product of all lengths in the shape. Arrays are defined inductively: any value (of a value or function type) can be used as an element of an array, but it is not possible for an array to contain itself as an element, or an array that contains itself, and so on. Types other than array are called <em>atomic types</em>, and their members are called <em>atoms</em>.</p> diff --git a/spec/types.md b/spec/types.md index 2e9860d9..eb9b8a1f 100644 --- a/spec/types.md +++ b/spec/types.md @@ -19,4 +19,4 @@ To begin the data types, a *character* is a [Unicode](https://en.wikipedia.org/w The precise type of a *number* may vary across BQN implementations or instances. A *real number* is a member of some supported subset of the [extended real numbers](https://en.wikipedia.org/wiki/Extended_real_number_line), that is, the real numbers and positive or negative infinity. Some system must be defined for rounding an arbitrary real number to a member of this subset, and the basic arithmetic operations add, subtract, multiply, divide, and natural exponent (base *e*) are defined by performing these operations on exact real values and rounding the result. The Power function (dyadic `β`) is also used but need not be exactly rounded. A *complex number* is a value with two real number *components*, a *real part* and an *imaginary part*. A BQN implementation can either support real numbers only, or complex numbers. -An *array* is a rectangular collection of data. It is defined by a *shape*, which is a list of non-negative integer lengths, and a *ravel*, which is a list of *elements* whose length (the array's *bound*) is the product of all lengths in the shape. Arrays are defined inductively: any value (of a value or function type) can be used as an element of an array, but it is not possible for an array to contain itself as an element, or an array that contains itself, and so on. +An *array* is a rectangular collection of data. It is defined by a *shape*, which is a list of non-negative integer lengths, and a *ravel*, which is a list of *elements* whose length (the array's *bound*) is the product of all lengths in the shape. Arrays are defined inductively: any value (of a value or function type) can be used as an element of an array, but it is not possible for an array to contain itself as an element, or an array that contains itself, and so on. Types other than array are called *atomic types*, and their members are called *atoms*. |
