diff options
| author | Marshall Lochbaum <mwlochbaum@gmail.com> | 2021-07-06 22:18:20 -0400 |
|---|---|---|
| committer | Marshall Lochbaum <mwlochbaum@gmail.com> | 2021-07-06 22:18:20 -0400 |
| commit | 4d602ea36183e62e463cea08900a16ea6240a03f (patch) | |
| tree | f21aef763d907d7e1b093cf633c544cfcdd0a085 | |
| parent | 97e31a20cefe21c1627f33057d20d7970511754f (diff) | |
Editing and links
| -rw-r--r-- | doc/functional.md | 14 | ||||
| -rw-r--r-- | doc/glossary.md | 7 | ||||
| -rw-r--r-- | doc/group.md | 20 | ||||
| -rw-r--r-- | doc/identity.md | 4 | ||||
| -rw-r--r-- | doc/indices.md | 16 | ||||
| -rw-r--r-- | doc/join.md | 4 | ||||
| -rw-r--r-- | doc/leading.md | 28 | ||||
| -rw-r--r-- | docs/doc/functional.html | 18 | ||||
| -rw-r--r-- | docs/doc/glossary.html | 7 | ||||
| -rw-r--r-- | docs/doc/group.html | 20 | ||||
| -rw-r--r-- | docs/doc/identity.html | 4 | ||||
| -rw-r--r-- | docs/doc/indices.html | 16 | ||||
| -rw-r--r-- | docs/doc/join.html | 6 | ||||
| -rw-r--r-- | docs/doc/leading.html | 28 |
14 files changed, 97 insertions, 95 deletions
diff --git a/doc/functional.md b/doc/functional.md index 86621699..0556d75e 100644 --- a/doc/functional.md +++ b/doc/functional.md @@ -4,7 +4,7 @@ BQN boasts of its functional capabilities, including first-class functions. What sort of functional support does it have, and how can a BQN programmer exercise these and out themself as a Schemer at heart? -First, let's be clear about what the terms we're using mean. A language has *first-class functions* when functions (however they are defined) can be used in all the same ways as "ordinary" values like numbers and so on, such as being passed as an argument or placed in a list. Lisp and JavaScript have first-class functions, C has unsafe first-class functions via function pointers, and Java and APL don't have them as functions can't be placed in lists or used as arguments. This doesn't mean every operation is supported on functions: for instance, numbers can be added, compared, and sorted; while functions could perhaps be added to give a train, comparing or sorting them as functions (not representations) isn't computable, and BQN doesn't support any of the three operations when passing functions as arguments. +First, let's be clear about what the terms we're using mean. A language has *first-class functions* when functions (however they are defined) can be used in all the same ways as "ordinary" values like numbers and so on, such as being passed as an argument or placed in a list. Lisp and JavaScript have first-class functions, C has unsafe first-class functions via function pointers, and Java 7 and APL don't have them as functions can't be placed in lists or used as arguments. This doesn't mean every operation is supported on functions: for instance, numbers can be added, compared, and sorted; while functions could perhaps be added to give a train, comparing or sorting them as functions (not representations) isn't computable, and BQN doesn't support any of the three operations when passing functions as arguments. Traditionally, APL has worked around its lack of first-class functions with operators, that is, second-order functions. Arrays in APL are first class while functions are second class and operators are third class, and each class can act on the ones before it. However, the three-tier system has some obvious limitations that we'll discuss, and BQN removes these by making every type first class. @@ -80,13 +80,13 @@ To ← { ⟩ --> -The term *functional programming* is more contentious, and has many meanings some of which can be vague. Here I use it for what might be called *first-class functional programming*, programming that makes significant use of first-class functions; in this usage, Scheme is probably the archetypal functional programming language. However, other definitions are also worth mentioning. APL is often called a functional programming language on the grounds that functions can be assigned and manipulated, and called recursively, all characteristics it shares with Lisp. I prefer the term *function-level programming* for this usage. A newer usage, which I call *pure functional programming*, restricts the term "function" to mathematical functions, which have no side effects, so that functional programming is programming with no side effects, often using monads to accumulate effects as part of arguments and results instead. Finally, *typed functional programming* is closely associated with pure functional programming and refers to statically-typed functional languages such as Haskell, F#, and Idris (the last of which even supports *dependently-typed functional programming*, but I already said "finally" so we'll stop there). Of these, BQN supports first-class functional and function-level programming, allows but doesn't encourage pure functional programming, and does not support typed functional programming, as it is dynamically and not statically typed. +The term *functional programming* is more contentious, and has many meanings some of which can be vague. Here I use it for what might be called *first-class functional programming*, programming that makes significant use of first-class functions; in this usage, Scheme is probably the archetypal functional programming language. However, other definitions are also worth mentioning. APL is often called a functional programming language on the grounds that functions can be assigned and manipulated, and called recursively, all characteristics it shares with Lisp. I prefer the term *function-level programming* for this usage. A newer usage, which I call *pure functional programming*, restricts the term "function" to mathematical functions, which have no side effects, so that functional programming is programming with no side effects, often using monads to accumulate effects as part of arguments and results instead. Finally, *typed functional programming* is closely associated with pure functional programming and refers to languages influenced by type theory such as Haskell, F#, and Idris (the last of which even supports *dependently-typed functional programming*, but I already said "finally" so we'll stop there). Of these, BQN supports first-class functional and function-level programming, allows but doesn't encourage pure functional programming, and does not support typed functional programming, as it's dynamically and not statically typed. Another topic we are interested in is *lexical scoping* and *closures*. Lexical scoping means that the realm in which a variable exists is determined by its containing context (in BQN, the surrounding set of curly braces `{}`, if any) within the source code. A closure is really an implementation mechanism, but it's often used to refer to a property of lexical scoping that appears when functions defined in a particular block can be accessed after the block finishes execution. For example, they might be returned from a function or assigned to a variable outside of that function's scope. In this case the functions can still access variables in the original scope. I consider this property to be a requirement for a correct lexical scoping implementation, but it's traditionally not a part of APL: implementation might not have lexical scoping (for example, J and I believe A+ use static scoping where functions can't access variables in containing scopes) or might cut off the scope once execution ends, leading to value errors that one wouldn't predict from the rules of lexical scoping. ## Functions in APL -This seems like a good place for a brief and entirely optional discussion of how APL handles functions and why it does it this way. As mentioned above, APL's functions are second class rather than first class. However, it's worth noting that the barriers to making functions first-class objects have been entirely syntactic and conceptual, not technical. In fact, the J language has for a long time had [a bug](http://www.jsoftware.com/pipermail/programming/2013-January/031260.html) that allows an array containing a function to be created: by selecting from the array, the function itself can even be passed through tacit functions as an argument! +This seems like a good place for a brief and entirely optional discussion of how APL handles functions and why it does it this way. As mentioned above, APL's functions are second class rather than first class. But the barriers to making functions first-class objects have been entirely syntactic and conceptual, not technical. In fact, the J language has for a long time had [a bug](http://www.jsoftware.com/pipermail/programming/2013-January/031260.html) that allows an array containing a function to be created: by selecting from the array, the function itself can even be passed through tacit functions as an argument! The primary reason why APL doesn't allow functions to be passed as arguments is probably syntax: in particular, there's no way to say that a function should be used as the left argument to another function, as an expression like `F G x` with functions `F` and `G` and an array `x` will simply be evaluated as two monadic function applications. However, there's no syntactic rule that prevents a function from returning a function, and Dyalog APL for example allows this (so `⍎'+'` returns the function `+`). Dyalog's `⎕OR` is another interesting phenomenon in this context: it creates an array from a function or operator, which can then be used as an element or argument like any array. The mechanism is essentially the same as BQN's first class functions, and in fact `⎕OR`s even share a form of BQN's [syntactic type erasure](../commentary/problems.md#syntactic-type-erasure), as a `⎕OR` of a function passed as an operand magically becomes a function again. But outside of this property, it's cumbersome and slow to convert functions to and from `⎕OR`s, so they don't work very well as a first-class function mechanism. @@ -107,9 +107,9 @@ First, let's look at the basics: a small program that has functions as its argum v0 + ((𝕏 1) - v0) × ⊢ } -We can pass it the exponential function as an argument by giving it the name `Exp` and then referring to it in lowercase (that is, in a subject role). The result is a train that adds 1 to *e*-1 times the argument. +We can pass it the [exponential](arithmetic.md#basic-arithmetic) function as an argument by giving it the name `Exp` and then referring to it in lowercase (that is, in a subject role). The result is a [train](train.md) that adds 1 to *e*-1 times the argument. - Lin ← { v0←𝕏0 ⋄ v0+((𝕏1)-v0)×⊢ } + Lin ← { v0←𝕏0 ⋄ v0+((𝕏1)-v0)×⊢ } # (copy of above) Exp ← ⋆ Lin exp @@ -135,12 +135,12 @@ Note also in this case that we could have used a modifier with a very similar de Its call syntax is simpler as well. In other cases, however, the function version might be preferable, for example when dealing with arrays of functions or many arguments including a function. - _lin ↩ { v0←𝔽0 ⋄ v0+((𝔽1)-v0)×⊢ } + _lin ↩ { v0←𝔽0 ⋄ v0+((𝔽1)-v0)×⊢ } # (copy again) Exp _lin 5 ### Arrays of functions -It's very convenient to put a function in an array, which is fortunate because this is one of the most important uses of functions as subjects. Here's an example of an array of functions with a reduction applied to it, composing them together. +It's very convenient to put a function in an array, which is fortunate because this is one of the most important uses of functions as subjects. Here's an example of an array of functions with a [fold](fold.md) applied to it, composing them together. {𝕎∘𝕏}´ ⋆‿-‿(ט) diff --git a/doc/glossary.md b/doc/glossary.md index d16f5584..9fd9a568 100644 --- a/doc/glossary.md +++ b/doc/glossary.md @@ -23,6 +23,7 @@ A few terms refer to multiple types collectively: * **Modifier**: A 1-modifier or 2-modifier. * **Data type**: Number, character, or array. * **Operation type**: Function, 1-modifier, or 2-modifier. +* **Mutable type**: Operation or namespace. BQN uses standard terminology for particular sets of numbers, with natural numbers starting at 0. * **Boolean**: 0 or 1. @@ -47,7 +48,7 @@ The possible roles are: * **Element**: One of the values contained in an array. * **Axis**: One dimension or direction in an array. * **Rank**: The number of dimensions an array has. -* **Shape**: The number of elements an array has along each dimension. +* [**Shape**](shape.md): The number of elements an array has along each dimension. * **Length**: The number of elements an array has along the first dimension, or 1 if it has rank 0. * [**Depth**](depth.md): The greatest number of times an element can be selected from a value before reaching an atom. * **Fill**: A "prototypical" array element used in certain operations; it's an inferred property of an array. @@ -60,7 +61,7 @@ The possible roles are: * **Frame**: A prefix of an array's shape, used for agreement with the Rank modifier. * **Unit**: An array of rank 0, or an atom. -* **Unit array**: An array of rank 0 other than an atom. +* [**Unit array**](enclose.md#whats-a-unit): An array of rank 0 other than an atom. * **List**: An array of rank 1. * **String**: A list of characters. * **Table**: An array of rank 2. @@ -92,7 +93,7 @@ The possible roles are: * **Token formation** or tokenization: Splitting source code into a sequence of tokens. * **Token**: A name, literal, primitive, or other syntactic element. * **Literal**: A token that indicates a fixed value of a data type. -* **Primitive**: One of several fixed operations defined by the language, denoted by a single-character token. +* [**Primitive**](primitive.md): One of several fixed operations defined by the language, denoted by a single-character token. * **Word**: A sequence of alphabetic or numeric characters. * **Name**: A word that starts with an alphabetic character. Names are compared case-insensitively and ignoring underscores `_`. * **Numeric literal**: A word that starts with a numeric character, indicating a number. diff --git a/doc/group.md b/doc/group.md index dac75506..de8d438e 100644 --- a/doc/group.md +++ b/doc/group.md @@ -2,7 +2,7 @@ # Group -BQN replaces the [Key](https://aplwiki.com/wiki/Key) operator from J or Dyalog APL, and [many forms of partitioning](https://aplwiki.com/wiki/Partition_representations), with a single (ambivalent) Group function `⊔`. This function is somewhat related to the K function `=` of the same name, but results in an array rather than a dictionary. +BQN replaces the Key operator from J or Dyalog APL, and [many forms of partitioning](https://aplwiki.com/wiki/Partition_representations), with a single (ambivalent) Group function `⊔`. This function is somewhat related to the K function `=` of the same name, but results in an array rather than a dictionary. <!--GEN Num ← ·Highlight •Repr @@ -89,7 +89,7 @@ Here, the index 2 appears at indices 0 and 3 while the index 3 appears at index ### Multidimensional grouping -Dyadic Group allows the right argument to be grouped along multiple axes by using a nested left argument. In this case, the left argument must be a list of numeric lists, and the result has rank `≠𝕨` while its elements—as always—have the same rank as `𝕩`. The result shape is `1+⌈´¨𝕨`, while the shape of element `i⊑𝕨⊔𝕩` is `i+´∘=¨𝕨`. If every element of `𝕨` is sorted ascending and contains only non-negative numbers, we have `𝕩≡∾𝕨⊔𝕩`, that is, Join is the inverse of Partition. +Dyadic Group allows the right argument to be grouped along multiple axes by using a nested left argument. In this case, the left argument must be a list of numeric lists, and the result has rank `≠𝕨` while its elements—as always—have the same rank as `𝕩`. The result shape is `1+⌈´¨𝕨`, while the shape of element `i⊑𝕨⊔𝕩` is `i+´∘=¨𝕨`. If every element of `𝕨` is sorted ascending and contains only non-negative numbers, we have `𝕩≡∾𝕨⊔𝕩`, that is, [Join](join.md#join) is the inverse of Partition. Here we split up a rank-2 array into a rank-2 array of rank-2 arrays. Along the first axis we simply separate the first pair and second pair of rows—a partition. Along the second axis we separate odd from even indices. @@ -101,31 +101,31 @@ The monadic case works similarly: Group Indices always satisfies `⊔𝕩 ←→ ## Properties -Group is closely related to the inverse of Indices, `/⁼`. In fact, inverse Indices called on the index argument gives the length of each group: +Group is closely related to the [inverse of Indices](replicate.md#inverse), `/⁼`. In fact, inverse Indices called on the index argument gives the length of each group: ≠¨⊔ 2‿3‿1‿2 /⁼∧ 2‿3‿1‿2 -A related fact is that calling Indices on the result of Group sorts all the indices passed to Group (removing any ¯1s). This is a kind of counting sort. +A related fact is that calling Indices on the result lengths of Group sorts all the indices passed to Group (removing any ¯1s). This is a kind of counting sort. /≠¨⊔ 2‿3‿1‿¯1‿2 -Called dyadically, Group sorts the right argument according to the left and adds some extra structure. If this structure is removed with Join, Group can be thought of as a kind of sorting. +Called dyadically, Group sorts the right argument according to the left and adds some extra structure. If this structure is removed with [Join](join.md#join), Group can be thought of as a kind of sorting. ∾ 2‿3‿1‿¯1‿2 ⊔ "abcde" 2‿3‿1‿¯1‿2 {F←(0≤𝕨)⊸/ ⋄ 𝕨⍋⊸⊏○F𝕩} "abcde" -Group can even be implemented with the same techniques as a bucket sort, which can be branchless and fast. +Group can even be implemented with the same [techniques](../implementation/primitive/sort.md#counting-and-bucket-sort) as a bucket sort, making it branchless and fast. ## Applications -The obvious application of Group is to group some values according to a known or computed property. If this property isn't an integer, it can be turned into one using Classify (monadic `⊐`, identical to `⍷⊸⊐`). Classify numbers the unique values in its argument by first occurrence. +The obvious application of Group is to group some values according to a known or computed property. If this property isn't a natural number, it can be turned into one using [Classify](selfcmp.md#classify) (`⊐`), which numbers the unique values in its argument by first occurrence. ln ← "Phelps"‿"Latynina"‿"Bjørgen"‿"Andrianov"‿"Bjørndalen" co ← "US" ‿"SU" ‿"NO" ‿"SU" ‿"NO" ≍˘ co ⊐⊸⊔ ln -If we would like a particular index to key correspondence, we can use a fixed left argument to Index Of. +If we would like a particular index to key correspondence, we can use a fixed left argument to [Index Of](search.md#index-of). countries ← "IT"‿"JP"‿"NO"‿"SU"‿"US" countries ≍˘ co countries⊸⊐⊸⊔ ln @@ -137,7 +137,7 @@ However, this solution will fail if there are trailing keys with no values. To f ### Partitioning -In examples we have been using a list of strings stranded together. Often it's more convenient to write the string with spaces, and split it up as part of the code. In this case, the index corresponding to each word (that is, each letter in the word) is the number of spaces before it. We can get this number of spaces from a prefix sum on the boolean list which is 1 at each space. +In examples we have been using a list of strings stranded together. Often it's more convenient to write the string with spaces, and split it up as part of the code. In this case, the index corresponding to each word (that is, each letter in the word) is the number of spaces before it. We can get this number of spaces from a Plus-[Scan](scan.md) on the boolean list which is 1 at each space. ' '(+`∘=⊔⊢)"BQN uses notation as a tool of thought" @@ -151,7 +151,7 @@ In other cases, we might want to split on spaces, so that words are separated by ' '((⊢-˜¬×+`)∘=⊔⊢)" string with spaces " -Trailing spaces are ignored because Group with equal-length arguments never produces trailing empty groups—to intentionally include them you'd replace `=` with `(=∾0˙)`. But in string processing we probably want to avoid empty words anywhere. To make this happen, we should increase the word index only once per group of spaces. We can do this by taking the prefix sum of a list that is 1 only for a space with no space before it. To make such a list, we can use the [Shift Before](shift.md) function, giving a list of previous elements. To treat the first element as if it's before a space (so that leading spaces have no effect rather than creating an initial empty group), we shift in a 1. +Trailing spaces are ignored because Group with equal-length arguments never produces trailing empty groups—to intentionally include them you'd replace `=` with `(=∾0˙)`. But in string processing we probably want to avoid empty words anywhere. To make this happen, we should increase the word index only once per group of spaces. We can do this by applying Plus Scan to a list that is 1 only for a space with no space before it. This list is produced using [Shift Before](shift.md) to get a list of previous elements. To treat the first element as though it's before a space (so that leading spaces have no effect rather than creating an initial empty group), we shift in a 1. (⊢≍1⊸»<⊢) ' '=" string with spaces " # All, then filtered, spaces ≍⟜(⊢-˜¬×·+`1⊸»<⊢)' '=" string with spaces " # More processing diff --git a/doc/identity.md b/doc/identity.md index b9cfd395..a9916bcb 100644 --- a/doc/identity.md +++ b/doc/identity.md @@ -18,11 +18,11 @@ Of course, it's easy to write block functions `{𝕩}` and `{𝕨}` that return ## Filling arrays -What's the easiest way to create a matrix with 0 on the first row, 1 on the second, and so on? Probably this one, with [table](map.md): +What's the easiest way to create a matrix with 0 on the first row, 1 on the second, and so on? Probably this one, with [table](map.md#table): (↕4) ⊣⌜ ↕5 -The right argument `↕5` could be any length-5 list, as its values aren't used. With `5⥊0`, we could use `+⌜` instead, but requiring a specific argument seems artificial. A similar pattern applies with Each: +The right argument `↕5` could be any length-5 list, as its values aren't used. With `5⥊0`, we could use `+⌜` instead, but requiring a specific argument seems artificial. A similar pattern applies with [Each](map.md#each): (⌽↕4) ⊣¨ ↕4‿5 diff --git a/doc/indices.md b/doc/indices.md index 105b9ee7..62122175 100644 --- a/doc/indices.md +++ b/doc/indices.md @@ -2,9 +2,9 @@ # 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. +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](range.md) (`↕`), which returns an array of them if given a list argument, and [Pick](pick.md) (`⊑`), 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. +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. [Group](group.md) (`⊔`) 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. | Monad | Dyad | Where | How |-------|------|---------|-------------------------- @@ -20,23 +20,23 @@ The following functions take or return indices. Except where marked, the indices | | `⊏` | `𝕨` | Major cell or along-axis number | `⍉` | | | Axis number -Dyadic Transpose (`⍉`) 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. +In Dyadic [Transpose](transpose.md#dyadic-transpose) (`⍉`), `𝕨` is made up of indices into axes of `𝕩`. Since array shape is 1-dimensional, there is only one sensible choice for these elements, a single number each. ## Element indices 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 atomic 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. +Functions [Range](range.md) (`↕`), [Indices](replicate.md) (`/`), [Group](group.md) (`⊔`), and [Pick](pick.md) (`⊑`) naturally deal with element indices. Each of these can be defined to use list indices. However, this usually rules out the possibility of using atomic 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 `/` to use atomic indices, which comes with the requirement that the arguments to Group and Indices, and the result of Group Indices, must be lists. For dyadic Group the depth-1 elements of `𝕨` are arrays of indices along axes of the result ([multi-axis documentation](group.md#multidimensional-grouping)). This means each axis of `𝕩` can only be related to one axis of the result. -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 atomic 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 units 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? +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 [Select](select.md) (`⊏`) handles atomic 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 `𝕨`. An atom `𝕨` in Pick 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 `𝕨` 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 (invalid index errors are of course still possible). 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 -One of the successes of the [leading axis model](https://aplwiki.com/wiki/Leading_axis_theory) is to introduce a kind of index for multidimensional arrays that is easier to work with than list indices. The model introduces [cells](https://aplwiki.com/wiki/Cell), 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. +One of the successes of the [leading axis model](https://aplwiki.com/wiki/Leading_axis_theory) is to introduce a kind of index for multidimensional arrays that is easier to work with than list indices. The model introduces [cells](https://aplwiki.com/wiki/Cell), 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. Such an index can also be considered to select along the first axis, since an index along any axis is a single number. -Ordering-based functions `⍋`, `⍒`, `⊐`, and `⊒` 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 `⊐` only uses the ordering in an incidental way, because it's defined to return the *first* 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. +[Ordering](order.md) functions `⍋⍒` and [search](search.md)/[self-comparison](selfcmp.md) functions `⊐⊒` that depend on cell ordering only really make sense with major cell indices: while other indices have an ordering, it's not very natural. Note that `⊐` only uses the ordering in an incidental way, because it's defined to return the *first* index where a cell in `𝕩` 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. -Only one other function—but an important one!—deals with cells rather than elements: `⊏`, cell selection. Like dyadic `↑↓↕⌽⍉` (depth 0) and `/⊔` (depth 1), Select allows either a simple first-axis case where the left argument has depth 1 or less (a depth-0 argument is automatically enclosed), and a multi-axis case where it is a list of depth-1 elements. In each case the depth-1 arrays index along a single axis. +Only one other function—but an important one!—deals with cells rather than elements: [Select](select.md) (`⊏`). Select [allows](leading.md#multiple-axes) either a simple first-axis case where `𝕨` has depth 1 or less (a depth-0 argument is automatically enclosed), and a multi-axis case where it is a list of depth-1 elements. In each case the depth-1 arrays index along a single axis. ## General cell indices diff --git a/doc/join.md b/doc/join.md index 50a817e7..f244db59 100644 --- a/doc/join.md +++ b/doc/join.md @@ -2,7 +2,7 @@ # Join and Join To -The glyph `∾` combines arrays along an existing axis, a concept that other languages might call "concatenation" or "catenation" but BQN names "Join". The one-argument form Join and two-argument form Join To are parallel to the [functions that combine arrays along a new axis](couple.md), Merge (`>`) and Couple (`≍`). +The glyph `∾` combines arrays along an existing axis, a concept that other languages might call "concatenation" or "catenation" but BQN names "Join". The one-argument form Join and two-argument form Join To are parallel to [the functions](couple.md) that combine arrays along a new axis, Merge (`>`) and Couple (`≍`). ## Join To @@ -18,7 +18,7 @@ If the arguments have the same rank, then they are combined along the first axis For this definition to work, major cells of `𝕨` and `𝕩` have to have the same shape. That means that `𝕨≡○(1↓≢)𝕩`, and the shape of the result is the sum of the lengths of `𝕨` and `𝕩` followed by their shared major cell shape: to use a self-referential definition, the final shape is given by `+○≠ ∾ ⊣⁼○(1↓≢)` for arguments of equal rank. - a ∾ 2‿5⥊b + a ∾ 2‿5⥊b # Shapes don't fit Join To will also allow arguments with ranks that are one apart. In this case, the smaller-rank argument is treated as a major cell in its entirety. If for example `𝕨<○=𝕩`, then we must have `(≢𝕨)≡1↓≢𝕩`, and the result shape is `1⊸+⌾⊑≢𝕩`. diff --git a/doc/leading.md b/doc/leading.md index 6a0020aa..467890df 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-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. +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](shape.md) (`≠`) counts these major cells, while [Prefixes](prefixes.md) (`↑`), Suffixes (`↓`), [Reverse](reverse.md) (`⌽`), and [First Cell](select.md) (`⊏`) move them around. The [Insert](fold.md#insert) (`˝`) and [Scan](scan.md) (`` ` ``) modifiers also yield functions that work along the first axis; in contrast, [Fold](fold.md) (`´`) requires `𝕩` 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 @@ -21,14 +21,14 @@ To use these functions on another axis, use the Rank (`⎉`) or Cells (`˘`) mod ⌽˘ a # Swap the columns ⊣`˘ a # Replicate along rows -In these three cases above, the results are the same as you would get from transposing before and after (this has no effect on the result of `⊏˘`, since it has rank 1). But in the following cases, the structure is quite different: `↑a` is a list of matrices while `↑˘a` is a matrix of lists. This is because the functions `⊏`, `⌽`, and `` ⊣` `` leave the trailing axis structure intact (`⊏` removes one axis); taking into account that Rank or Cells always preserves the leading or frame axes, all axes are preserved (except the one removed by `⊏`). In contrast, Prefixes or Suffixes pushes some axes down in depth, and the number of axes that are pushed down in this way changes with the rank of application. More precisely, these functions move axes after the first from the argument itself to result elements, and create two axes from the first axis, with one of them forming the sole result axis and the other joining the rest as an element axis. +In these three cases above, the results are the same as you would get from [transposing](transpose.md) before and after (this has no effect on the result of `⊏˘`, since it has rank 1). But in the following cases, the structure is quite different: `↑a` is a list of matrices while `↑˘a` is a matrix of lists. This is because the functions `⊏`, `⌽`, and `` ⊣` `` leave the trailing axis structure intact (`⊏` removes one axis); taking into account that Rank or Cells always preserves the leading or frame axes, all axes are preserved (except the one removed by `⊏`). In contrast, Prefixes or Suffixes will push some axes down in depth, and the number of axes that are pushed down in this way changes with the rank of application. More precisely, these functions move axes after the first from the argument itself to result elements, and create two axes from the first axis, with one of them forming the sole result axis and the other joining the rest as an element axis. ↑ a # Prefixes of a: ranks 1|2 ↑˘ a # Prefixes of rows: ranks 2|1 ∾˝ a # Join the cells ∾˝˘ a # Join-insert is a no-op on lists -[Solo](couple.md) (`≍`), something of a maverick, manages to act on *zero* leading axes of its argument by creating the first axis of the *result* instead. Because it doesn't need any axis to work, it can go in front of either axis but also past the last one by working with rank 0, a case where most array functions would give an error. +[Solo](couple.md) (`≍`), something of a maverick, manages to act on *zero* leading axes of `𝕩` by creating the first axis of the *result* instead. Because it doesn't need any axis to work, it can go in front of either axis but also past the last one by working with rank 0, a case where most array functions would give an error. ≢ ≍ a # Solo adds a length-1 axis a ≡ ⊏ ≍ a # First Cell undoes this @@ -37,7 +37,7 @@ In these three cases above, the results are the same as you would get from trans ### Comparing cells -The functions in the last section manipulate cells in the same way regardless of what data they contain. Other functions compare cells to each other, either testing whether they match or how they are ordered relative to one another. The two Grade functions `⍋⍒`, and the self-comparison functions Classify (`⊐`), Mark Firsts (`∊`), and Occurrence Count (`⊒`), each give a list result, with one number for each cell. We can see below that Occurrence Count returns the same results even as we make the argument cells more complicated, because the changes made preserve the matching of cells. +The functions in the last section manipulate cells in the same way regardless of what data they contain. Other functions compare cells to each other, either testing whether they match or how they are ordered relative to one another. The two [Grade](order.md) functions `⍋⍒`, and the [self-comparison](selfcmp.md) functions Classify (`⊐`), Mark Firsts (`∊`), and Occurrence Count (`⊒`), each give a list result, with one number for each cell. We can see below that Occurrence Count returns the same results even as we make the argument cells more complicated, because the changes made preserve the matching of cells. s ← "abracadabra" ⊒ s @@ -51,30 +51,30 @@ The two Sort functions `∧∨` and Deduplicate (`⍷`) move cells around based ### Other monadic functions -Not all functions work on the first axis in a straightforward manner. [Transpose](transpose.md) `⍉` moves the first axis to the end, so while it focuses on the first one, it shifts every axis of the argument. [Join](join.md) `∾` also works on every axis of its argument, and applies to the leading axes of the argument's *elements* instead: these leading inner axes are matched up with the outer axes, and trailing inner axes are allowed but the elements must have rank at least as high as the argument array. +Not all functions work on the first axis in a straightforward manner. [Transpose](transpose.md) `⍉` moves the first axis to the end, so while it focuses on the first one, it shifts every axis of `𝕩`. [Join](join.md) `∾` also works on every axis of its argument, and applies to the leading axes of `𝕩`'s *elements* instead: these leading inner axes are matched up with the outer axes, and trailing inner axes are allowed but the elements must have rank at least as high as the argument array. -The other two monadic functions that work on high-rank arguments are Deshape (`⥊`) and First (`⊑`). These treat the argument as one long list, ordered by its element indices. This ordering privileges leading axes (in fact, it is the reason for the choice of leading axes in the leading axis convention), but these functions can't really be said to work on leading axes: they apply to all axes. +The other two monadic functions that work on high-rank arguments are [Deshape](reshape.md#deshape) (`⥊`) and [First](pick.md#first) (`⊑`). These treat `𝕩` as one long list, ordered by its element indices. This ordering privileges leading axes (in fact, it is the reason for the choice of leading axes in the leading axis convention), but these functions can't really be said to work on leading axes: they apply to all axes. -The Each (`¨`) and Table (`⌜`) modifiers return functions which are the same in the monadic case. These functions simply go through all elements of the argument array without regard for its multi-dimensional structure (the operand is applied to elements in index order, matching Deshape; this matters if it has side effects). Similarly, monadic arithmetic functions do not have any sort of leading axis dependence. +The [Each](map.md) (`¨`) and [Table](map.md#table) (`⌜`) modifiers return functions which are the same in the monadic case. These functions simply go through all elements of the argument array without regard for its multi-dimensional structure (the operand is applied to elements in index order, matching Deshape; this matters if it has side effects). Similarly, monadic arithmetic functions do not have any sort of leading axis dependence. ## Dyadic functions -For dyadic functions the pattern of working on only one argument axis is not so common. Only two functions can be said to follow it roughly: Join to (`∾`) combines two arrays along one axis, using the first axis of both arguments if they have the same rank and of the higher-rank argument if they differ by one. [Couple](couple.md) (`≍`), like Solo, does not manipulate the argument axes but adds a result axis. There are also some functions that can't be limited to leading axes: [Reshape](reshape.md) (`⥊`) treats the argument as one long list, and Pick (`⊑`) requires each index to be as long as the right argument's rank, because it selects elements and not cells from the right argument. +For dyadic functions the pattern of working on only one argument axis is not so common. Only two functions can be said to follow it roughly: [Join to](join.md) (`∾`) combines two arrays along one axis, using the first axis of both arguments if they have the same rank and of the higher-rank argument if they differ by one. [Couple](couple.md) (`≍`), like Solo, does not manipulate the argument axes but adds a result axis. There are also some functions that can't be limited to leading axes: [Reshape](reshape.md) (`⥊`) treats `𝕩` as one long list, and [Pick](pick.md) (`⊑`) requires each index to be as long as `𝕩`'s rank, because it selects elements and not cells from `𝕩`. ### Multiple axes -Instead of always working on a single axis, many dyadic functions work on one axis by default, but also allow a left argument with multiple elements corresponding to leading axes of the right argument. To decide which of the two possibilities applies, these functions test the left argument depth, a convention that is discussed in the [depth](depth.md#testing-depth-for-multiple-axis-primitives) documentation. A left argument that applies to one axis has a particular depth; the argument can also be a list of such arguments. +Instead of always working on a single axis, many dyadic functions work on one axis by default, but also allow a left argument with multiple elements corresponding to leading axes of `𝕩`. To decide which of the two possibilities applies, these functions test the depth of `𝕨`, a convention that is discussed in the [depth](depth.md#testing-depth-for-multiple-axis-primitives) documentation. A left argument that applies to one axis has a particular depth; `𝕨` can also be a list of such arguments. | Single-axis depth | Functions |-------------------|---------- | 0 | `↑↓↕⌽⍉` | 1 | `/⊏⊔` -Functions such as Take and Drop use a single number per axis. When the left argument is a list of numbers, they apply to initial axes. Observing the operation of Rotate on the result of Range is instructive: +Functions such as Take and Drop use a single number per axis. When `𝕨` is a list of numbers, they apply to initial axes. Observing the operation of [Rotate](reverse.md#rotate) on the result of [Range](range.md) is instructive: 2‿1 ⌽ ↕3‿5 -The array is shifted once to the left and twice upward, so that the first index (by ravel order) is now `⊑2‿1⌽↕3‿5 ←→ 2‿1`. To see how values are matched to leading axes, we can look at how Drop changes the shape of its argument: +The array is shifted once to the left and twice upward, so that the first index (by ravel order) is now `⊑2‿1⌽↕3‿5 ←→ 2‿1`. To see how values are matched to leading axes, we can look at how [Drop](take.md) changes the shape of its argument: ≢ 3‿2 ↓ 7‿7‿7‿7⥊"abc" @@ -82,7 +82,7 @@ Functions with single-axis depth 1 tend to be more complicated; see for example ### Leading axis agreement -Arithmetic functions, and the Each (`¨`) and Depth (`⚇`) modifiers, use leading axis agreement to match their arguments together. All axes of the lower-rank argument are matched with the leading axes of the higher-rank one, and axes matched together must have the same length. After pairing axes in this way, a single element of the lower-rank argument might correspond to any number of elements of the higher-rank one. It's reused for each of those corresponding elements. +[Arithmetic](arithmetic.md) functions, and the [Each](map.md#each) (`¨`) and [Depth](depth.md#the-depth-modifier) (`⚇`) modifiers, use leading axis agreement to match their arguments together. All axes of the lower-rank argument are matched with the leading axes of the higher-rank one, and axes matched together must have the same length. After pairing axes in this way, a single element of the lower-rank argument might correspond to any number of elements of the higher-rank one. It's reused for each of those corresponding elements. ⊢ x ← 3‿2‿4 ⥊ ↕60 # A rank-3 array 100‿0‿200 + x # 0-cells paired with 2-cells @@ -96,11 +96,11 @@ With leading axis agreement, there are `k+1` shapes for arrays that can be added ### Search functions -The search functions Bins (`⍋⍒`), Index of (`⊐`), Progressive Index of (`⊒`), and Member of (`∊`) look through cells of one argument to find cells of the other. Find (`⍷`) also does a search, but a slightly different one: it tries to find *slices* of cells of its right argument that match the left argument. +The [search functions](search.md) Bins (`⍋⍒`), Index of (`⊐`), Progressive Index of (`⊒`), and Member of (`∊`) look through cells of one argument to find cells of the other. Find (`⍷`) also does a search, but a slightly different one: it tries to find *slices* of cells of `𝕩` that match `𝕨`. | Searching through | Look for | Functions |-------------------|----------|---------- | `𝕨` | `𝕩` | `⍋⍒⊐⊒` | `𝕩` | `𝕨` | `∊⍷` -For all of these functions but Find, the argument to search through is treated as a list of its major cells. It is the rank of these major cells—let's call this rank `c`—that determines how the other argument is treated. That argument must have rank at least `c`, and it is treated as an array of `c`-cells. For example, if the left argument to `⍋` is a matrix, then each 1-cell or row of the right argument is treated independently, and each one yields one number in the result: a 0-cell. The result rank of `⍋` is always `𝕨¬○=𝕩`. +For all of these functions but Find, the argument to search through is treated as a list of its major cells. It is the rank of these major cells—let's call this rank `c`—that determines how the other argument is treated. That argument must have rank at least `c`, and it is treated as an array of `c`-cells. For example, if the left argument to `⍋` is a matrix, then each 1-cell or row of `𝕩` is treated independently, and each one yields one number in the result: a 0-cell. The result rank of `⍋` is always `𝕨¬○=𝕩`. diff --git a/docs/doc/functional.html b/docs/doc/functional.html index 6cf2a402..f0149660 100644 --- a/docs/doc/functional.html +++ b/docs/doc/functional.html @@ -6,7 +6,7 @@ <div class="nav"><a href="https://github.com/mlochbaum/BQN">BQN</a> / <a href="../index.html">main</a> / <a href="index.html">doc</a></div> <h1 id="functional-programming">Functional programming</h1> <p>BQN boasts of its functional capabilities, including first-class functions. What sort of functional support does it have, and how can a BQN programmer exercise these and out themself as a Schemer at heart?</p> -<p>First, let's be clear about what the terms we're using mean. A language has <em>first-class functions</em> when functions (however they are defined) can be used in all the same ways as "ordinary" values like numbers and so on, such as being passed as an argument or placed in a list. Lisp and JavaScript have first-class functions, C has unsafe first-class functions via function pointers, and Java and APL don't have them as functions can't be placed in lists or used as arguments. This doesn't mean every operation is supported on functions: for instance, numbers can be added, compared, and sorted; while functions could perhaps be added to give a train, comparing or sorting them as functions (not representations) isn't computable, and BQN doesn't support any of the three operations when passing functions as arguments.</p> +<p>First, let's be clear about what the terms we're using mean. A language has <em>first-class functions</em> when functions (however they are defined) can be used in all the same ways as "ordinary" values like numbers and so on, such as being passed as an argument or placed in a list. Lisp and JavaScript have first-class functions, C has unsafe first-class functions via function pointers, and Java 7 and APL don't have them as functions can't be placed in lists or used as arguments. This doesn't mean every operation is supported on functions: for instance, numbers can be added, compared, and sorted; while functions could perhaps be added to give a train, comparing or sorting them as functions (not representations) isn't computable, and BQN doesn't support any of the three operations when passing functions as arguments.</p> <p>Traditionally, APL has worked around its lack of first-class functions with operators, that is, second-order functions. Arrays in APL are first class while functions are second class and operators are third class, and each class can act on the ones before it. However, the three-tier system has some obvious limitations that we'll discuss, and BQN removes these by making every type first class.</p> <svg viewBox='0 0 512 512'> <g font-size='18px' text-anchor='middle' fill='currentColor'> @@ -59,10 +59,10 @@ </g> </svg> -<p>The term <em>functional programming</em> is more contentious, and has many meanings some of which can be vague. Here I use it for what might be called <em>first-class functional programming</em>, programming that makes significant use of first-class functions; in this usage, Scheme is probably the archetypal functional programming language. However, other definitions are also worth mentioning. APL is often called a functional programming language on the grounds that functions can be assigned and manipulated, and called recursively, all characteristics it shares with Lisp. I prefer the term <em>function-level programming</em> for this usage. A newer usage, which I call <em>pure functional programming</em>, restricts the term "function" to mathematical functions, which have no side effects, so that functional programming is programming with no side effects, often using monads to accumulate effects as part of arguments and results instead. Finally, <em>typed functional programming</em> is closely associated with pure functional programming and refers to statically-typed functional languages such as Haskell, F#, and Idris (the last of which even supports <em>dependently-typed functional programming</em>, but I already said "finally" so we'll stop there). Of these, BQN supports first-class functional and function-level programming, allows but doesn't encourage pure functional programming, and does not support typed functional programming, as it is dynamically and not statically typed.</p> +<p>The term <em>functional programming</em> is more contentious, and has many meanings some of which can be vague. Here I use it for what might be called <em>first-class functional programming</em>, programming that makes significant use of first-class functions; in this usage, Scheme is probably the archetypal functional programming language. However, other definitions are also worth mentioning. APL is often called a functional programming language on the grounds that functions can be assigned and manipulated, and called recursively, all characteristics it shares with Lisp. I prefer the term <em>function-level programming</em> for this usage. A newer usage, which I call <em>pure functional programming</em>, restricts the term "function" to mathematical functions, which have no side effects, so that functional programming is programming with no side effects, often using monads to accumulate effects as part of arguments and results instead. Finally, <em>typed functional programming</em> is closely associated with pure functional programming and refers to languages influenced by type theory such as Haskell, F#, and Idris (the last of which even supports <em>dependently-typed functional programming</em>, but I already said "finally" so we'll stop there). Of these, BQN supports first-class functional and function-level programming, allows but doesn't encourage pure functional programming, and does not support typed functional programming, as it's dynamically and not statically typed.</p> <p>Another topic we are interested in is <em>lexical scoping</em> and <em>closures</em>. Lexical scoping means that the realm in which a variable exists is determined by its containing context (in BQN, the surrounding set of curly braces <code><span class='Brace'>{}</span></code>, if any) within the source code. A closure is really an implementation mechanism, but it's often used to refer to a property of lexical scoping that appears when functions defined in a particular block can be accessed after the block finishes execution. For example, they might be returned from a function or assigned to a variable outside of that function's scope. In this case the functions can still access variables in the original scope. I consider this property to be a requirement for a correct lexical scoping implementation, but it's traditionally not a part of APL: implementation might not have lexical scoping (for example, J and I believe A+ use static scoping where functions can't access variables in containing scopes) or might cut off the scope once execution ends, leading to value errors that one wouldn't predict from the rules of lexical scoping.</p> <h2 id="functions-in-apl">Functions in APL</h2> -<p>This seems like a good place for a brief and entirely optional discussion of how APL handles functions and why it does it this way. As mentioned above, APL's functions are second class rather than first class. However, it's worth noting that the barriers to making functions first-class objects have been entirely syntactic and conceptual, not technical. In fact, the J language has for a long time had <a href="http://www.jsoftware.com/pipermail/programming/2013-January/031260.html">a bug</a> that allows an array containing a function to be created: by selecting from the array, the function itself can even be passed through tacit functions as an argument!</p> +<p>This seems like a good place for a brief and entirely optional discussion of how APL handles functions and why it does it this way. As mentioned above, APL's functions are second class rather than first class. But the barriers to making functions first-class objects have been entirely syntactic and conceptual, not technical. In fact, the J language has for a long time had <a href="http://www.jsoftware.com/pipermail/programming/2013-January/031260.html">a bug</a> that allows an array containing a function to be created: by selecting from the array, the function itself can even be passed through tacit functions as an argument!</p> <p>The primary reason why APL doesn't allow functions to be passed as arguments is probably syntax: in particular, there's no way to say that a function should be used as the left argument to another function, as an expression like <code><span class='Function'>F</span> <span class='Function'>G</span> <span class='Value'>x</span></code> with functions <code><span class='Function'>F</span></code> and <code><span class='Function'>G</span></code> and an array <code><span class='Value'>x</span></code> will simply be evaluated as two monadic function applications. However, there's no syntactic rule that prevents a function from returning a function, and Dyalog APL for example allows this (so <code><span class='Value'>⍎</span><span class='String'>'+'</span></code> returns the function <code><span class='Function'>+</span></code>). Dyalog's <code><span class='Value'>⎕</span><span class='Function'>OR</span></code> is another interesting phenomenon in this context: it creates an array from a function or operator, which can then be used as an element or argument like any array. The mechanism is essentially the same as BQN's first class functions, and in fact <code><span class='Value'>⎕</span><span class='Function'>OR</span></code>s even share a form of BQN's <a href="../commentary/problems.html#syntactic-type-erasure">syntactic type erasure</a>, as a <code><span class='Value'>⎕</span><span class='Function'>OR</span></code> of a function passed as an operand magically becomes a function again. But outside of this property, it's cumbersome and slow to convert functions to and from <code><span class='Value'>⎕</span><span class='Function'>OR</span></code>s, so they don't work very well as a first-class function mechanism.</p> <p>Another reason for APL's reluctance to adopt first-class functions is that Iverson and others seemed to believe that functions fundamentally are not a kind of data, because it's impossible to uniquely represent, compare, and order them. One effect of this viewpoint is J's gerund mechanism, which converts a function to an array representation, primarily so that lists of gerunds can be created. Gerunds are nested arrays containing character vectors at the leaves, so they are arrays as Iverson thought of them. However, I consider this conversion of functions to arrays, intended to avoid arrays that contain "black box" functions, to be a mistake: while it doesn't compromise the purity of arrays, it gives the illusion that a function corresponds to a particular array, which is not true from the mathematical perspective of functions as mappings from an arbitrary input to an output. I also think the experience of countless languages with first-class functions shows that there is no practical issue with arrays that contain functions. While having all arrays be concrete entities with a unique canonical representation seems desirable, I don't find the existence of arrays without this property to be detract from working with arrays that do have it.</p> <h2 id="functional-programming-in-bqn">Functional programming in BQN</h2> @@ -75,9 +75,9 @@ <span class='Value'>v0</span> <span class='Function'>+</span> <span class='Paren'>((</span><span class='Function'>𝕏</span> <span class='Number'>1</span><span class='Paren'>)</span> <span class='Function'>-</span> <span class='Value'>v0</span><span class='Paren'>)</span> <span class='Function'>×</span> <span class='Function'>⊢</span> <span class='Brace'>}</span> </pre> -<p>We can pass it the exponential function as an argument by giving it the name <code><span class='Function'>Exp</span></code> and then referring to it in lowercase (that is, in a subject role). The result is a train that adds 1 to <em>e</em>-1 times the argument.</p> -<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=TGluIOKGkCB7IHYw4oaQ8J2VjzAg4ouEIHYwKygo8J2VjzEpLXYwKcOX4oqiIH0KRXhwIOKGkCDii4YKTGluIGV4cA==">↗️</a><pre> <span class='Function'>Lin</span> <span class='Gets'>←</span> <span class='Brace'>{</span> <span class='Value'>v0</span><span class='Gets'>←</span><span class='Function'>𝕏</span><span class='Number'>0</span> <span class='Separator'>⋄</span> <span class='Value'>v0</span><span class='Function'>+</span><span class='Paren'>((</span><span class='Function'>𝕏</span><span class='Number'>1</span><span class='Paren'>)</span><span class='Function'>-</span><span class='Value'>v0</span><span class='Paren'>)</span><span class='Function'>×⊢</span> <span class='Brace'>}</span> - <span class='Function'>Exp</span> <span class='Gets'>←</span> <span class='Function'>⋆</span> +<p>We can pass it the <a href="arithmetic.html#basic-arithmetic">exponential</a> function as an argument by giving it the name <code><span class='Function'>Exp</span></code> and then referring to it in lowercase (that is, in a subject role). The result is a <a href="train.html">train</a> that adds 1 to <em>e</em>-1 times the argument.</p> +<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=TGluIOKGkCB7IHYw4oaQ8J2VjzAg4ouEIHYwKygo8J2VjzEpLXYwKcOX4oqiIH0gICMgKGNvcHkgb2YgYWJvdmUpCkV4cCDihpAg4ouGCkxpbiBleHA=">↗️</a><pre> <span class='Function'>Lin</span> <span class='Gets'>←</span> <span class='Brace'>{</span> <span class='Value'>v0</span><span class='Gets'>←</span><span class='Function'>𝕏</span><span class='Number'>0</span> <span class='Separator'>⋄</span> <span class='Value'>v0</span><span class='Function'>+</span><span class='Paren'>((</span><span class='Function'>𝕏</span><span class='Number'>1</span><span class='Paren'>)</span><span class='Function'>-</span><span class='Value'>v0</span><span class='Paren'>)</span><span class='Function'>×⊢</span> <span class='Brace'>}</span> <span class='Comment'># (copy of above) +</span> <span class='Function'>Exp</span> <span class='Gets'>←</span> <span class='Function'>⋆</span> <span class='Function'>Lin</span> <span class='Value'>exp</span> 1+1.718281828459045×⊢ </pre> @@ -101,12 +101,12 @@ <span class='Brace'>}</span> </pre> <p>Its call syntax is simpler as well. In other cases, however, the function version might be preferable, for example when dealing with arrays of functions or many arguments including a function.</p> -<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=X2xpbiDihqkgeyB2MOKGkPCdlL0wIOKLhCB2MCsoKPCdlL0xKS12MCnDl+KKoiB9CkV4cCBfbGluIDU=">↗️</a><pre> <span class='Modifier'>_lin</span> <span class='Gets'>↩</span> <span class='Brace'>{</span> <span class='Value'>v0</span><span class='Gets'>←</span><span class='Function'>𝔽</span><span class='Number'>0</span> <span class='Separator'>⋄</span> <span class='Value'>v0</span><span class='Function'>+</span><span class='Paren'>((</span><span class='Function'>𝔽</span><span class='Number'>1</span><span class='Paren'>)</span><span class='Function'>-</span><span class='Value'>v0</span><span class='Paren'>)</span><span class='Function'>×⊢</span> <span class='Brace'>}</span> - <span class='Function'>Exp</span> <span class='Modifier'>_lin</span> <span class='Number'>5</span> +<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=X2xpbiDihqkgeyB2MOKGkPCdlL0wIOKLhCB2MCsoKPCdlL0xKS12MCnDl+KKoiB9ICAjIChjb3B5IGFnYWluKQpFeHAgX2xpbiA1">↗️</a><pre> <span class='Modifier'>_lin</span> <span class='Gets'>↩</span> <span class='Brace'>{</span> <span class='Value'>v0</span><span class='Gets'>←</span><span class='Function'>𝔽</span><span class='Number'>0</span> <span class='Separator'>⋄</span> <span class='Value'>v0</span><span class='Function'>+</span><span class='Paren'>((</span><span class='Function'>𝔽</span><span class='Number'>1</span><span class='Paren'>)</span><span class='Function'>-</span><span class='Value'>v0</span><span class='Paren'>)</span><span class='Function'>×⊢</span> <span class='Brace'>}</span> <span class='Comment'># (copy again) +</span> <span class='Function'>Exp</span> <span class='Modifier'>_lin</span> <span class='Number'>5</span> 9.591409142295225 </pre> <h3 id="arrays-of-functions">Arrays of functions</h3> -<p>It's very convenient to put a function in an array, which is fortunate because this is one of the most important uses of functions as subjects. Here's an example of an array of functions with a reduction applied to it, composing them together.</p> +<p>It's very convenient to put a function in an array, which is fortunate because this is one of the most important uses of functions as subjects. Here's an example of an array of functions with a <a href="fold.html">fold</a> applied to it, composing them together.</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=e/CdlY7iiJjwnZWPfcK0IOKLhuKAvy3igL8ow5fLnCk=">↗️</a><pre> <span class='Brace'>{</span><span class='Function'>𝕎</span><span class='Modifier2'>∘</span><span class='Function'>𝕏</span><span class='Brace'>}</span><span class='Modifier'>´</span> <span class='Function'>⋆</span><span class='Ligature'>‿</span><span class='Function'>-</span><span class='Ligature'>‿</span><span class='Paren'>(</span><span class='Function'>×</span><span class='Modifier'>˜</span><span class='Paren'>)</span> ⋆∘(-∘(ט)) </pre> diff --git a/docs/doc/glossary.html b/docs/doc/glossary.html index 02f6d1da..f1c8d39e 100644 --- a/docs/doc/glossary.html +++ b/docs/doc/glossary.html @@ -27,6 +27,7 @@ <li><strong>Modifier</strong>: A 1-modifier or 2-modifier.</li> <li><strong>Data type</strong>: Number, character, or array.</li> <li><strong>Operation type</strong>: Function, 1-modifier, or 2-modifier.</li> +<li><strong>Mutable type</strong>: Operation or namespace.</li> </ul> <p>BQN uses standard terminology for particular sets of numbers, with natural numbers starting at 0.</p> <ul> @@ -53,7 +54,7 @@ <li><strong>Element</strong>: One of the values contained in an array.</li> <li><strong>Axis</strong>: One dimension or direction in an array.</li> <li><strong>Rank</strong>: The number of dimensions an array has.</li> -<li><strong>Shape</strong>: The number of elements an array has along each dimension.</li> +<li><a href="shape.html"><strong>Shape</strong></a>: The number of elements an array has along each dimension.</li> <li><strong>Length</strong>: The number of elements an array has along the first dimension, or 1 if it has rank 0.</li> <li><a href="depth.html"><strong>Depth</strong></a>: The greatest number of times an element can be selected from a value before reaching an atom.</li> <li><strong>Fill</strong>: A "prototypical" array element used in certain operations; it's an inferred property of an array.</li> @@ -68,7 +69,7 @@ </ul> <ul> <li><strong>Unit</strong>: An array of rank 0, or an atom.</li> -<li><strong>Unit array</strong>: An array of rank 0 other than an atom.</li> +<li><a href="enclose.html#whats-a-unit"><strong>Unit array</strong></a>: An array of rank 0 other than an atom.</li> <li><strong>List</strong>: An array of rank 1.</li> <li><strong>String</strong>: A list of characters.</li> <li><strong>Table</strong>: An array of rank 2.</li> @@ -103,7 +104,7 @@ <li><strong>Token formation</strong> or tokenization: Splitting source code into a sequence of tokens.</li> <li><strong>Token</strong>: A name, literal, primitive, or other syntactic element.</li> <li><strong>Literal</strong>: A token that indicates a fixed value of a data type.</li> -<li><strong>Primitive</strong>: One of several fixed operations defined by the language, denoted by a single-character token.</li> +<li><a href="primitive.html"><strong>Primitive</strong></a>: One of several fixed operations defined by the language, denoted by a single-character token.</li> <li><strong>Word</strong>: A sequence of alphabetic or numeric characters.</li> <li><strong>Name</strong>: A word that starts with an alphabetic character. Names are compared case-insensitively and ignoring underscores <code><span class='Modifier2'>_</span></code>.</li> <li><strong>Numeric literal</strong>: A word that starts with a numeric character, indicating a number.</li> diff --git a/docs/doc/group.html b/docs/doc/group.html index 37d5a5ed..a6f4b341 100644 --- a/docs/doc/group.html +++ b/docs/doc/group.html @@ -5,7 +5,7 @@ </head> <div class="nav"><a href="https://github.com/mlochbaum/BQN">BQN</a> / <a href="../index.html">main</a> / <a href="index.html">doc</a></div> <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> +<p>BQN replaces the Key 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> <svg viewBox='-344 -121.8 640 226.8'> <g text-anchor='middle' font-family='BQN,monospace'> <rect class='code' stroke-width='1' rx='12' x='-280' y='-102.2' width='512' height='204.4'/> @@ -103,7 +103,7 @@ </pre> <p>Here, the index 2 appears at indices 0 and 3 while the index 3 appears at index 1.</p> <h3 id="multidimensional-grouping">Multidimensional grouping</h3> -<p>Dyadic Group allows the right argument to be grouped along multiple axes by using a nested left argument. In this case, the left argument must be a list of numeric lists, and the result has rank <code><span class='Function'>≠</span><span class='Value'>𝕨</span></code> while its elements—as always—have the same rank as <code><span class='Value'>𝕩</span></code>. The result shape is <code><span class='Number'>1</span><span class='Function'>+⌈</span><span class='Modifier'>´¨</span><span class='Value'>𝕨</span></code>, while the shape of element <code><span class='Value'>i</span><span class='Function'>⊑</span><span class='Value'>𝕨</span><span class='Function'>⊔</span><span class='Value'>𝕩</span></code> is <code><span class='Value'>i</span><span class='Function'>+</span><span class='Modifier'>´</span><span class='Modifier2'>∘</span><span class='Function'>=</span><span class='Modifier'>¨</span><span class='Value'>𝕨</span></code>. If every element of <code><span class='Value'>𝕨</span></code> is sorted ascending and contains only non-negative numbers, we have <code><span class='Value'>𝕩</span><span class='Function'>≡∾</span><span class='Value'>𝕨</span><span class='Function'>⊔</span><span class='Value'>𝕩</span></code>, that is, Join is the inverse of Partition.</p> +<p>Dyadic Group allows the right argument to be grouped along multiple axes by using a nested left argument. In this case, the left argument must be a list of numeric lists, and the result has rank <code><span class='Function'>≠</span><span class='Value'>𝕨</span></code> while its elements—as always—have the same rank as <code><span class='Value'>𝕩</span></code>. The result shape is <code><span class='Number'>1</span><span class='Function'>+⌈</span><span class='Modifier'>´¨</span><span class='Value'>𝕨</span></code>, while the shape of element <code><span class='Value'>i</span><span class='Function'>⊑</span><span class='Value'>𝕨</span><span class='Function'>⊔</span><span class='Value'>𝕩</span></code> is <code><span class='Value'>i</span><span class='Function'>+</span><span class='Modifier'>´</span><span class='Modifier2'>∘</span><span class='Function'>=</span><span class='Modifier'>¨</span><span class='Value'>𝕨</span></code>. If every element of <code><span class='Value'>𝕨</span></code> is sorted ascending and contains only non-negative numbers, we have <code><span class='Value'>𝕩</span><span class='Function'>≡∾</span><span class='Value'>𝕨</span><span class='Function'>⊔</span><span class='Value'>𝕩</span></code>, that is, <a href="join.html#join">Join</a> is the inverse of Partition.</p> <p>Here we split up a rank-2 array into a rank-2 array of rank-2 arrays. Along the first axis we simply separate the first pair and second pair of rows—a partition. Along the second axis we separate odd from even indices.</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4p+oMOKAvzDigL8x4oC/MSww4oC/MeKAvzDigL8x4oC/MOKAvzHigL8w4p+pIOKKlCAoMTDDl+KGlTQpK+KMnOKGlTc=">↗️</a><pre> <span class='Bracket'>⟨</span><span class='Number'>0</span><span class='Ligature'>‿</span><span class='Number'>0</span><span class='Ligature'>‿</span><span class='Number'>1</span><span class='Ligature'>‿</span><span class='Number'>1</span><span class='Separator'>,</span><span class='Number'>0</span><span class='Ligature'>‿</span><span class='Number'>1</span><span class='Ligature'>‿</span><span class='Number'>0</span><span class='Ligature'>‿</span><span class='Number'>1</span><span class='Ligature'>‿</span><span class='Number'>0</span><span class='Ligature'>‿</span><span class='Number'>1</span><span class='Ligature'>‿</span><span class='Number'>0</span><span class='Bracket'>⟩</span> <span class='Function'>⊔</span> <span class='Paren'>(</span><span class='Number'>10</span><span class='Function'>×↕</span><span class='Number'>4</span><span class='Paren'>)</span><span class='Function'>+</span><span class='Modifier'>⌜</span><span class='Function'>↕</span><span class='Number'>7</span> ┌─ @@ -120,25 +120,25 @@ <p>Each group <code><span class='Value'>i</span><span class='Function'>⊑</span><span class='Value'>𝕨</span><span class='Function'>⊔</span><span class='Value'>𝕩</span></code> is composed of the cells <code><span class='Value'>j</span><span class='Function'><</span><span class='Modifier'>¨</span><span class='Modifier2'>⊸</span><span class='Function'>⊏</span><span class='Value'>𝕩</span></code> such that <code><span class='Value'>i</span><span class='Function'>≢</span><span class='Value'>j</span><span class='Function'>⊑</span><span class='Modifier'>¨</span><span class='Value'>𝕨</span></code>. The groups retain their array structure and ordering along each argument axis. Using multidimensional Replicate we can say that <code><span class='Value'>i</span><span class='Function'>⊑</span><span class='Value'>𝕨</span><span class='Function'>⊔</span><span class='Value'>𝕩</span></code> is <code><span class='Paren'>(</span><span class='Value'>i</span><span class='Function'>=</span><span class='Value'>𝕨</span><span class='Paren'>)</span><span class='Function'>/</span><span class='Value'>𝕩</span></code>.</p> <p>The monadic case works similarly: Group Indices always satisfies <code><span class='Function'>⊔</span><span class='Value'>𝕩</span> <span class='Gets'>←→</span> <span class='Value'>𝕩</span><span class='Function'>⊔↕≠</span><span class='Modifier2'>⚇</span><span class='Number'>1</span><span class='Value'>𝕩</span></code>. As with <code><span class='Function'>↕</span></code>, the depth of the result of Group Indices is always one greater than that of its argument. A depth-0 argument is not allowed.</p> <h2 id="properties">Properties</h2> -<p>Group is closely related to the inverse of Indices, <code><span class='Function'>/</span><span class='Modifier'>⁼</span></code>. In fact, inverse Indices called on the index argument gives the length of each group:</p> +<p>Group is closely related to the <a href="replicate.html#inverse">inverse of Indices</a>, <code><span class='Function'>/</span><span class='Modifier'>⁼</span></code>. In fact, inverse Indices called on the index argument gives the length of each group:</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4omgwqjiipQgMuKAvzPigL8x4oC/Mgov4oG84oinIDLigL8z4oC/MeKAvzI=">↗️</a><pre> <span class='Function'>≠</span><span class='Modifier'>¨</span><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'>1</span><span class='Ligature'>‿</span><span class='Number'>2</span> ⟨ 0 1 2 1 ⟩ <span class='Function'>/</span><span class='Modifier'>⁼</span><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'>1</span><span class='Ligature'>‿</span><span class='Number'>2</span> ⟨ 0 1 2 1 ⟩ </pre> -<p>A related fact is that calling Indices on the result of Group sorts all the indices passed to Group (removing any ¯1s). This is a kind of counting sort.</p> +<p>A related fact is that calling Indices on the result lengths of Group sorts all the indices passed to Group (removing any ¯1s). This is a kind of counting sort.</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=L+KJoMKo4oqUIDLigL8z4oC/MeKAv8KvMeKAvzI=">↗️</a><pre> <span class='Function'>/≠</span><span class='Modifier'>¨</span><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'>1</span><span class='Ligature'>‿</span><span class='Number'>¯1</span><span class='Ligature'>‿</span><span class='Number'>2</span> ⟨ 1 2 2 3 ⟩ </pre> -<p>Called dyadically, Group sorts the right argument according to the left and adds some extra structure. If this structure is removed with Join, Group can be thought of as a kind of sorting.</p> +<p>Called dyadically, Group sorts the right argument according to the left and adds some extra structure. If this structure is removed with <a href="join.html#join">Join</a>, Group can be thought of as a kind of sorting.</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4oi+IDLigL8z4oC/MeKAv8KvMeKAvzIg4oqUICJhYmNkZSIKMuKAvzPigL8x4oC/wq8x4oC/MiB7RuKGkCgw4omk8J2VqCniirgvIOKLhCDwnZWo4o2L4oq44oqP4peLRvCdlal9ICJhYmNkZSI=">↗️</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'>1</span><span class='Ligature'>‿</span><span class='Number'>¯1</span><span class='Ligature'>‿</span><span class='Number'>2</span> <span class='Function'>⊔</span> <span class='String'>"abcde"</span> "caeb" <span class='Number'>2</span><span class='Ligature'>‿</span><span class='Number'>3</span><span class='Ligature'>‿</span><span class='Number'>1</span><span class='Ligature'>‿</span><span class='Number'>¯1</span><span class='Ligature'>‿</span><span class='Number'>2</span> <span class='Brace'>{</span><span class='Function'>F</span><span class='Gets'>←</span><span class='Paren'>(</span><span class='Number'>0</span><span class='Function'>≤</span><span class='Value'>𝕨</span><span class='Paren'>)</span><span class='Modifier2'>⊸</span><span class='Function'>/</span> <span class='Separator'>⋄</span> <span class='Value'>𝕨</span><span class='Function'>⍋</span><span class='Modifier2'>⊸</span><span class='Function'>⊏</span><span class='Modifier2'>○</span><span class='Function'>F</span><span class='Value'>𝕩</span><span class='Brace'>}</span> <span class='String'>"abcde"</span> "caeb" </pre> -<p>Group can even be implemented with the same techniques as a bucket sort, which can be branchless and fast.</p> +<p>Group can even be implemented with the same <a href="../implementation/primitive/sort.html#counting-and-bucket-sort">techniques</a> as a bucket sort, making it branchless and fast.</p> <h2 id="applications">Applications</h2> -<p>The obvious application of Group is to group some values according to a known or computed property. If this property isn't an integer, it can be turned into one using Classify (monadic <code><span class='Function'>⊐</span></code>, identical to <code><span class='Function'>⍷</span><span class='Modifier2'>⊸</span><span class='Function'>⊐</span></code>). Classify numbers the unique values in its argument by first occurrence.</p> +<p>The obvious application of Group is to group some values according to a known or computed property. If this property isn't a natural number, it can be turned into one using <a href="selfcmp.html#classify">Classify</a> (<code><span class='Function'>⊐</span></code>), which numbers the unique values in its argument by first occurrence.</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=bG4g4oaQICJQaGVscHMi4oC/IkxhdHluaW5hIuKAvyJCasO4cmdlbiLigL8iQW5kcmlhbm92IuKAvyJCasO4cm5kYWxlbiIKY28g4oaQICJVUyIgICAg4oC/IlNVIiAgICAgIOKAvyJOTyIgICAgIOKAvyJTVSIgICAgICAg4oC/Ik5PIgriiY3LmCBjbyDiipDiirjiipQgbG4=">↗️</a><pre> <span class='Value'>ln</span> <span class='Gets'>←</span> <span class='String'>"Phelps"</span><span class='Ligature'>‿</span><span class='String'>"Latynina"</span><span class='Ligature'>‿</span><span class='String'>"Bjørgen"</span><span class='Ligature'>‿</span><span class='String'>"Andrianov"</span><span class='Ligature'>‿</span><span class='String'>"Bjørndalen"</span> <span class='Value'>co</span> <span class='Gets'>←</span> <span class='String'>"US"</span> <span class='Ligature'>‿</span><span class='String'>"SU"</span> <span class='Ligature'>‿</span><span class='String'>"NO"</span> <span class='Ligature'>‿</span><span class='String'>"SU"</span> <span class='Ligature'>‿</span><span class='String'>"NO"</span> <span class='Function'>≍</span><span class='Modifier'>˘</span> <span class='Value'>co</span> <span class='Function'>⊐</span><span class='Modifier2'>⊸</span><span class='Function'>⊔</span> <span class='Value'>ln</span> @@ -148,7 +148,7 @@ ⟨ "Bjørgen" "Bjørndalen" ⟩ ┘ </pre> -<p>If we would like a particular index to key correspondence, we can use a fixed left argument to Index Of.</p> +<p>If we would like a particular index to key correspondence, we can use a fixed left argument to <a href="search.html#index-of">Index Of</a>.</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=Y291bnRyaWVzIOKGkCAiSVQi4oC/IkpQIuKAvyJOTyLigL8iU1Ui4oC/IlVTIgpjb3VudHJpZXMg4omNy5ggY28gY291bnRyaWVz4oq44oqQ4oq44oqUIGxu">↗️</a><pre> <span class='Value'>countries</span> <span class='Gets'>←</span> <span class='String'>"IT"</span><span class='Ligature'>‿</span><span class='String'>"JP"</span><span class='Ligature'>‿</span><span class='String'>"NO"</span><span class='Ligature'>‿</span><span class='String'>"SU"</span><span class='Ligature'>‿</span><span class='String'>"US"</span> <span class='Value'>countries</span> <span class='Function'>≍</span><span class='Modifier'>˘</span> <span class='Value'>co</span> <span class='Value'>countries</span><span class='Modifier2'>⊸</span><span class='Function'>⊐</span><span class='Modifier2'>⊸</span><span class='Function'>⊔</span> <span class='Value'>ln</span> ┌─ @@ -172,7 +172,7 @@ ┘ </pre> <h3 id="partitioning">Partitioning</h3> -<p>In examples we have been using a list of strings stranded together. Often it's more convenient to write the string with spaces, and split it up as part of the code. In this case, the index corresponding to each word (that is, each letter in the word) is the number of spaces before it. We can get this number of spaces from a prefix sum on the boolean list which is 1 at each space.</p> +<p>In examples we have been using a list of strings stranded together. Often it's more convenient to write the string with spaces, and split it up as part of the code. In this case, the index corresponding to each word (that is, each letter in the word) is the number of spaces before it. We can get this number of spaces from a Plus-<a href="scan.html">Scan</a> on the boolean list which is 1 at each space.</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=JyAnKCtg4oiYPeKKlOKKoikiQlFOIHVzZXMgbm90YXRpb24gYXMgYSB0b29sIG9mIHRob3VnaHQi">↗️</a><pre> <span class='String'>' '</span><span class='Paren'>(</span><span class='Function'>+</span><span class='Modifier'>`</span><span class='Modifier2'>∘</span><span class='Function'>=⊔⊢</span><span class='Paren'>)</span><span class='String'>"BQN uses notation as a tool of thought"</span> ⟨ "BQN" " uses" " notation" " as" " a" " tool" " of" " thought" ⟩ </pre> @@ -185,7 +185,7 @@ <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=JyAnKCjiiqIty5zCrMOXK2Ap4oiYPeKKlOKKoikiICBzdHJpbmcgd2l0aCAgc3BhY2VzICAgIg==">↗️</a><pre> <span class='String'>' '</span><span class='Paren'>((</span><span class='Function'>⊢-</span><span class='Modifier'>˜</span><span class='Function'>¬×+</span><span class='Modifier'>`</span><span class='Paren'>)</span><span class='Modifier2'>∘</span><span class='Function'>=⊔⊢</span><span class='Paren'>)</span><span class='String'>" string with spaces "</span> ⟨ ⟨⟩ ⟨⟩ "string" "with" ⟨⟩ "spaces" ⟩ </pre> -<p>Trailing spaces are ignored because Group with equal-length arguments never produces trailing empty groups—to intentionally include them you'd replace <code><span class='Function'>=</span></code> with <code><span class='Paren'>(</span><span class='Function'>=∾</span><span class='Number'>0</span><span class='Modifier'>˙</span><span class='Paren'>)</span></code>. But in string processing we probably want to avoid empty words anywhere. To make this happen, we should increase the word index only once per group of spaces. We can do this by taking the prefix sum of a list that is 1 only for a space with no space before it. To make such a list, we can use the <a href="shift.html">Shift Before</a> function, giving a list of previous elements. To treat the first element as if it's before a space (so that leading spaces have no effect rather than creating an initial empty group), we shift in a 1.</p> +<p>Trailing spaces are ignored because Group with equal-length arguments never produces trailing empty groups—to intentionally include them you'd replace <code><span class='Function'>=</span></code> with <code><span class='Paren'>(</span><span class='Function'>=∾</span><span class='Number'>0</span><span class='Modifier'>˙</span><span class='Paren'>)</span></code>. But in string processing we probably want to avoid empty words anywhere. To make this happen, we should increase the word index only once per group of spaces. We can do this by applying Plus Scan to a list that is 1 only for a space with no space before it. This list is produced using <a href="shift.html">Shift Before</a> to get a list of previous elements. To treat the first element as though it's before a space (so that leading spaces have no effect rather than creating an initial empty group), we shift in a 1.</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=KOKKouKJjTHiirjCuzziiqIpICcgJz0iICBzdHJpbmcgd2l0aCAgc3BhY2VzICAgIiAgIyBBbGwsIHRoZW4gZmlsdGVyZWQsIHNwYWNlcwriiY3in5wo4oqiLcucwqzDl8K3K2Ax4oq4wrs84oqiKScgJz0iICBzdHJpbmcgd2l0aCAgc3BhY2VzICAgIiAgIyBNb3JlIHByb2Nlc3NpbmcKJyAnKCjiiqIty5zCrMOXwrcrYDHiirjCuzziiqIp4oiYPeKKlOKKoikiICBzdHJpbmcgd2l0aCAgc3BhY2VzICAgIiAgIyBGaW5hbCByZXN1bHQKCicgJygowqwty5ziiqLDl8K3K2DCu+KKuD4p4oiY4omg4oqU4oqiKSIgIHN0cmluZyB3aXRoICBzcGFjZXMgICAiICAjIFNsaWdodGx5IHNob3J0ZXI=">↗️</a><pre> <span class='Paren'>(</span><span class='Function'>⊢≍</span><span class='Number'>1</span><span class='Modifier2'>⊸</span><span class='Function'>»<⊢</span><span class='Paren'>)</span> <span class='String'>' '</span><span class='Function'>=</span><span class='String'>" string with spaces "</span> <span class='Comment'># All, then filtered, spaces </span>┌─ ╵ 1 1 0 0 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 1 1 1 diff --git a/docs/doc/identity.html b/docs/doc/identity.html index f865e960..196b219c 100644 --- a/docs/doc/identity.html +++ b/docs/doc/identity.html @@ -21,7 +21,7 @@ <p>Depending on your past experiences, this could cause some confusion: built-in support for functions that do nothing? Documentation should say why a feature's there and how to use it, not just what it does, so we'll try to address this below. The most important single use is for tacit programming, but there are a variety of other uses as well.</p> <p>Of course, it's easy to write block functions <code><span class='Brace'>{</span><span class='Value'>𝕩</span><span class='Brace'>}</span></code> and <code><span class='Brace'>{</span><span class='Value'>𝕨</span><span class='Brace'>}</span></code> that return particular arguments. While I would already make <code><span class='Function'>⊣</span></code> and <code><span class='Function'>⊢</span></code> primitives just because they are common and important, there are also specific disadvantages to using blocks. They fail to indicate that there are no side effects, as primitives would, and they also need special casing for the interpreter to manipulate them when applying Undo (<code><span class='Modifier'>⁼</span></code>) or making other inferences.</p> <h2 id="filling-arrays">Filling arrays</h2> -<p>What's the easiest way to create a matrix with 0 on the first row, 1 on the second, and so on? Probably this one, with <a href="map.html">table</a>:</p> +<p>What's the easiest way to create a matrix with 0 on the first row, 1 on the second, and so on? Probably this one, with <a href="map.html#table">table</a>:</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=KOKGlTQpIOKKo+KMnCDihpU1">↗️</a><pre> <span class='Paren'>(</span><span class='Function'>↕</span><span class='Number'>4</span><span class='Paren'>)</span> <span class='Function'>⊣</span><span class='Modifier'>⌜</span> <span class='Function'>↕</span><span class='Number'>5</span> ┌─ ╵ 0 0 0 0 0 @@ -30,7 +30,7 @@ 3 3 3 3 3 ┘ </pre> -<p>The right argument <code><span class='Function'>↕</span><span class='Number'>5</span></code> could be any length-5 list, as its values aren't used. With <code><span class='Number'>5</span><span class='Function'>⥊</span><span class='Number'>0</span></code>, we could use <code><span class='Function'>+</span><span class='Modifier'>⌜</span></code> instead, but requiring a specific argument seems artificial. A similar pattern applies with Each:</p> +<p>The right argument <code><span class='Function'>↕</span><span class='Number'>5</span></code> could be any length-5 list, as its values aren't used. With <code><span class='Number'>5</span><span class='Function'>⥊</span><span class='Number'>0</span></code>, we could use <code><span class='Function'>+</span><span class='Modifier'>⌜</span></code> instead, but requiring a specific argument seems artificial. A similar pattern applies with <a href="map.html#each">Each</a>:</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=KOKMveKGlTQpIOKKo8KoIOKGlTTigL81">↗️</a><pre> <span class='Paren'>(</span><span class='Function'>⌽↕</span><span class='Number'>4</span><span class='Paren'>)</span> <span class='Function'>⊣</span><span class='Modifier'>¨</span> <span class='Function'>↕</span><span class='Number'>4</span><span class='Ligature'>‿</span><span class='Number'>5</span> ┌─ ╵ 3 3 3 3 3 diff --git a/docs/doc/indices.html b/docs/doc/indices.html index 9617caad..42938172 100644 --- a/docs/doc/indices.html +++ b/docs/doc/indices.html @@ -5,8 +5,8 @@ </head> <div class="nav"><a href="https://github.com/mlochbaum/BQN">BQN</a> / <a href="../index.html">main</a> / <a href="index.html">doc</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 (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> +<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: <a href="range.html">Range</a> (<code><span class='Function'>↕</span></code>), which returns an array of them if given a list argument, and <a href="pick.html">Pick</a> (<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. <a href="group.html">Group</a> (<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> <tr> @@ -85,15 +85,15 @@ </tr> </tbody> </table> -<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> +<p>In Dyadic <a href="transpose.html#dyadic-transpose">Transpose</a> (<code><span class='Function'>⍉</span></code>), <code><span class='Value'>𝕨</span></code> is made up of indices into axes of <code><span class='Value'>𝕩</span></code>. Since array shape is 1-dimensional, there is only one sensible choice for these elements, a single number each.</p> <h2 id="element-indices">Element indices</h2> <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 atomic 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 atomic 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 units 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> +<p>Functions <a href="range.html">Range</a> (<code><span class='Function'>↕</span></code>), <a href="replicate.html">Indices</a> (<code><span class='Function'>/</span></code>), <a href="group.html">Group</a> (<code><span class='Function'>⊔</span></code>), and <a href="pick.html">Pick</a> (<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 atomic 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 <code><span class='Function'>/</span></code> to use atomic indices, which comes with the requirement that the arguments to Group and Indices, and the result of Group Indices, must be lists. For dyadic Group the depth-1 elements of <code><span class='Value'>𝕨</span></code> are arrays of indices along axes of the result (<a href="group.html#multidimensional-grouping">multi-axis documentation</a>). This means each axis of <code><span class='Value'>𝕩</span></code> can only be related to one axis of the result.</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 <a href="select.html">Select</a> (<code><span class='Function'>⊏</span></code>) handles atomic 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 <code><span class='Value'>𝕨</span></code>. An atom <code><span class='Value'>𝕨</span></code> in Pick 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 <code><span class='Value'>𝕨</span></code> 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 (invalid index errors are of course still possible). 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> <h2 id="major-cell-indices">Major cell indices</h2> -<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> -<p>Only one other function—but an important one!—deals with cells rather than elements: <code><span class='Function'>⊏</span></code>, cell selection. Like dyadic <code><span class='Function'>↑↓↕⌽⍉</span></code> (depth 0) and <code><span class='Function'>/⊔</span></code> (depth 1), Select allows either a simple first-axis case where the left argument has depth 1 or less (a depth-0 argument is automatically enclosed), and a multi-axis case where it is a list of depth-1 elements. In each case the depth-1 arrays index along a single axis.</p> +<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. Such an index can also be considered to select along the first axis, since an index along any axis is a single number.</p> +<p><a href="order.html">Ordering</a> functions <code><span class='Function'>⍋⍒</span></code> and <a href="search.html">search</a>/<a href="selfcmp.html">self-comparison</a> functions <code><span class='Function'>⊐⊒</span></code> that depend on cell ordering only really make sense with major cell indices: while other indices have an ordering, it's not very natural. 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 cell in <code><span class='Value'>𝕩</span></code> 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> +<p>Only one other function—but an important one!—deals with cells rather than elements: <a href="select.html">Select</a> (<code><span class='Function'>⊏</span></code>). Select <a href="leading.html#multiple-axes">allows</a> either a simple first-axis case where <code><span class='Value'>𝕨</span></code> has depth 1 or less (a depth-0 argument is automatically enclosed), and a multi-axis case where it is a list of depth-1 elements. In each case the depth-1 arrays index along a single axis.</p> <h2 id="general-cell-indices">General cell indices</h2> <p>BQN does not use general cell indices directly, but it is useful to consider how they might work, and how a programmer might implement functions that use them in BQN if needed. The functions <code><span class='Function'>/</span></code>, <code><span class='Function'>⊔</span></code>, and <code><span class='Function'>⊏</span></code> are the ones that can work with indices for multidimensional arrays but don't already. Here we will examine how multidimensional versions would work.</p> <p>A cell index into an array of rank <code><span class='Value'>r</span></code> is a numeric list of length <code><span class='Value'>l</span><span class='Function'>≤</span><span class='Value'>r</span></code>, which then refers to a cell of rank <code><span class='Value'>r</span><span class='Function'>-</span><span class='Value'>l</span></code>. In BQN, the cell at index <code><span class='Value'>i</span></code> of array <code><span class='Value'>a</span></code> is <code><span class='Value'>i</span><span class='Function'><</span><span class='Modifier'>¨</span><span class='Modifier2'>⊸</span><span class='Function'>⊏</span><span class='Value'>a</span></code>.</p> diff --git a/docs/doc/join.html b/docs/doc/join.html index d9cac584..c3de72ae 100644 --- a/docs/doc/join.html +++ b/docs/doc/join.html @@ -5,7 +5,7 @@ </head> <div class="nav"><a href="https://github.com/mlochbaum/BQN">BQN</a> / <a href="../index.html">main</a> / <a href="index.html">doc</a></div> <h1 id="join-and-join-to">Join and Join To</h1> -<p>The glyph <code><span class='Function'>∾</span></code> combines arrays along an existing axis, a concept that other languages might call "concatenation" or "catenation" but BQN names "Join". The one-argument form Join and two-argument form Join To are parallel to the <a href="couple.html">functions that combine arrays along a new axis</a>, Merge (<code><span class='Function'>></span></code>) and Couple (<code><span class='Function'>≍</span></code>).</p> +<p>The glyph <code><span class='Function'>∾</span></code> combines arrays along an existing axis, a concept that other languages might call "concatenation" or "catenation" but BQN names "Join". The one-argument form Join and two-argument form Join To are parallel to <a href="couple.html">the functions</a> that combine arrays along a new axis, Merge (<code><span class='Function'>></span></code>) and Couple (<code><span class='Function'>≍</span></code>).</p> <h2 id="join-to">Join To</h2> <p>Join To connects its two arguments together, for example to join two strings:</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=ImFiY2QiIOKIviAiRUZHIg==">↗️</a><pre> <span class='String'>"abcd"</span> <span class='Function'>∾</span> <span class='String'>"EFG"</span> @@ -33,8 +33,8 @@ ┘ </pre> <p>For this definition to work, major cells of <code><span class='Value'>𝕨</span></code> and <code><span class='Value'>𝕩</span></code> have to have the same shape. That means that <code><span class='Value'>𝕨</span><span class='Function'>≡</span><span class='Modifier2'>○</span><span class='Paren'>(</span><span class='Number'>1</span><span class='Function'>↓≢</span><span class='Paren'>)</span><span class='Value'>𝕩</span></code>, and the shape of the result is the sum of the lengths of <code><span class='Value'>𝕨</span></code> and <code><span class='Value'>𝕩</span></code> followed by their shared major cell shape: to use a self-referential definition, the final shape is given by <code><span class='Function'>+</span><span class='Modifier2'>○</span><span class='Function'>≠</span> <span class='Function'>∾</span> <span class='Function'>⊣</span><span class='Modifier'>⁼</span><span class='Modifier2'>○</span><span class='Paren'>(</span><span class='Number'>1</span><span class='Function'>↓≢</span><span class='Paren'>)</span></code> for arguments of equal rank.</p> -<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=YSDiiL4gMuKAvzXipYpi">↗️</a><pre> <span class='Value'>a</span> <span class='Function'>∾</span> <span class='Number'>2</span><span class='Ligature'>‿</span><span class='Number'>5</span><span class='Function'>⥊</span><span class='Value'>b</span> -ERROR +<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=YSDiiL4gMuKAvzXipYpiICAjIFNoYXBlcyBkb24ndCBmaXQ=">↗️</a><pre> <span class='Value'>a</span> <span class='Function'>∾</span> <span class='Number'>2</span><span class='Ligature'>‿</span><span class='Number'>5</span><span class='Function'>⥊</span><span class='Value'>b</span> <span class='Comment'># Shapes don't fit +</span>ERROR </pre> <p>Join To will also allow arguments with ranks that are one apart. In this case, the smaller-rank argument is treated as a major cell in its entirety. If for example <code><span class='Value'>𝕨</span><span class='Function'><</span><span class='Modifier2'>○</span><span class='Function'>=</span><span class='Value'>𝕩</span></code>, then we must have <code><span class='Paren'>(</span><span class='Function'>≢</span><span class='Value'>𝕨</span><span class='Paren'>)</span><span class='Function'>≡</span><span class='Number'>1</span><span class='Function'>↓≢</span><span class='Value'>𝕩</span></code>, and the result shape is <code><span class='Number'>1</span><span class='Modifier2'>⊸</span><span class='Function'>+</span><span class='Modifier2'>⌾</span><span class='Function'>⊑≢</span><span class='Value'>𝕩</span></code>.</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=NOKAvzLigL8z4oC/MCDiiL4gYQ==">↗️</a><pre> <span class='Number'>4</span><span class='Ligature'>‿</span><span class='Number'>2</span><span class='Ligature'>‿</span><span class='Number'>3</span><span class='Ligature'>‿</span><span class='Number'>0</span> <span class='Function'>∾</span> <span class='Value'>a</span> diff --git a/docs/doc/leading.html b/docs/doc/leading.html index e7fd7fcb..72ce1450 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-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> +<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 <a href="shape.html">Length</a> (<code><span class='Function'>≠</span></code>) counts these major cells, while <a href="prefixes.html">Prefixes</a> (<code><span class='Function'>↑</span></code>), Suffixes (<code><span class='Function'>↓</span></code>), <a href="reverse.html">Reverse</a> (<code><span class='Function'>⌽</span></code>), and <a href="select.html">First Cell</a> (<code><span class='Function'>⊏</span></code>) move them around. The <a href="fold.html#insert">Insert</a> (<code><span class='Modifier'>˝</span></code>) and <a href="scan.html">Scan</a> (<code><span class='Modifier'>`</span></code>) modifiers also yield functions that work along the first axis; in contrast, <a href="fold.html">Fold</a> (<code><span class='Modifier'>´</span></code>) requires <code><span class='Value'>𝕩</span></code> 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=">↗️</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 @@ -46,7 +46,7 @@ ee" ┘ </pre> -<p>In these three cases above, the results are the same as you would get from transposing before and after (this has no effect on the result of <code><span class='Function'>⊏</span><span class='Modifier'>˘</span></code>, since it has rank 1). But in the following cases, the structure is quite different: <code><span class='Function'>↑</span><span class='Value'>a</span></code> is a list of matrices while <code><span class='Function'>↑</span><span class='Modifier'>˘</span><span class='Value'>a</span></code> is a matrix of lists. This is because the functions <code><span class='Function'>⊏</span></code>, <code><span class='Function'>⌽</span></code>, and <code><span class='Function'>⊣</span><span class='Modifier'>`</span></code> leave the trailing axis structure intact (<code><span class='Function'>⊏</span></code> removes one axis); taking into account that Rank or Cells always preserves the leading or frame axes, all axes are preserved (except the one removed by <code><span class='Function'>⊏</span></code>). In contrast, Prefixes or Suffixes pushes some axes down in depth, and the number of axes that are pushed down in this way changes with the rank of application. More precisely, these functions move axes after the first from the argument itself to result elements, and create two axes from the first axis, with one of them forming the sole result axis and the other joining the rest as an element axis.</p> +<p>In these three cases above, the results are the same as you would get from <a href="transpose.html">transposing</a> before and after (this has no effect on the result of <code><span class='Function'>⊏</span><span class='Modifier'>˘</span></code>, since it has rank 1). But in the following cases, the structure is quite different: <code><span class='Function'>↑</span><span class='Value'>a</span></code> is a list of matrices while <code><span class='Function'>↑</span><span class='Modifier'>˘</span><span class='Value'>a</span></code> is a matrix of lists. This is because the functions <code><span class='Function'>⊏</span></code>, <code><span class='Function'>⌽</span></code>, and <code><span class='Function'>⊣</span><span class='Modifier'>`</span></code> leave the trailing axis structure intact (<code><span class='Function'>⊏</span></code> removes one axis); taking into account that Rank or Cells always preserves the leading or frame axes, all axes are preserved (except the one removed by <code><span class='Function'>⊏</span></code>). In contrast, Prefixes or Suffixes will push some axes down in depth, and the number of axes that are pushed down in this way changes with the rank of application. More precisely, these functions move axes after the first from the argument itself to result elements, and create two axes from the first axis, with one of them forming the sole result axis and the other joining the rest as an element axis.</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4oaRIGEgICAgICAgICAgICAgICAgICAgIyBQcmVmaXhlcyBvZiBhOiAgICByYW5rcyAxfDIK4oaRy5ggYSAgICAgICAgICAgICAgICAgICMgUHJlZml4ZXMgb2Ygcm93czogcmFua3MgMnwxCuKIvsudIGEgICAgICAgICAgICAgICAgICAjIEpvaW4gdGhlIGNlbGxzCuKIvsudy5ggYSAgICAgICAgICAgICAgICAgIyBKb2luLWluc2VydCBpcyBhIG5vLW9wIG9uIGxpc3Rz">↗️</a><pre> <span class='Function'>↑</span> <span class='Value'>a</span> <span class='Comment'># Prefixes of a: ranks 1|2 </span>┌─ · ↕0‿2 ┌─ ┌─ ┌─ @@ -70,7 +70,7 @@ ef" ┘ </pre> -<p><a href="couple.html">Solo</a> (<code><span class='Function'>≍</span></code>), something of a maverick, manages to act on <em>zero</em> leading axes of its argument by creating the first axis of the <em>result</em> instead. Because it doesn't need any axis to work, it can go in front of either axis but also past the last one by working with rank 0, a case where most array functions would give an error.</p> +<p><a href="couple.html">Solo</a> (<code><span class='Function'>≍</span></code>), something of a maverick, manages to act on <em>zero</em> leading axes of <code><span class='Value'>𝕩</span></code> by creating the first axis of the <em>result</em> instead. Because it doesn't need any axis to work, it can go in front of either axis but also past the last one by working with rank 0, a case where most array functions would give an error.</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4omiIOKJjSBhICAgICAgICAgICAgICAgICAjIFNvbG8gYWRkcyBhIGxlbmd0aC0xIGF4aXMKYSDiiaEg4oqPIOKJjSBhICAgICAgICAgICAgICMgRmlyc3QgQ2VsbCB1bmRvZXMgdGhpcwriiaIg4omNy5ggYSAgICAgICAgICAgICAgICAjIFNvbG8gY2FuIGluc2VydCB0aGUgYXhpcyBkZWVwZXLigKYK4omiIOKJjeKOiTAgYSAgICAgICAgICAgICAgICMg4oCmb3IgZGVlcGVyIHN0aWxsLg==">↗️</a><pre> <span class='Function'>≢</span> <span class='Function'>≍</span> <span class='Value'>a</span> <span class='Comment'># Solo adds a length-1 axis </span>⟨ 1 3 2 ⟩ <span class='Value'>a</span> <span class='Function'>≡</span> <span class='Function'>⊏</span> <span class='Function'>≍</span> <span class='Value'>a</span> <span class='Comment'># First Cell undoes this @@ -81,7 +81,7 @@ </span>⟨ 3 2 1 ⟩ </pre> <h3 id="comparing-cells">Comparing cells</h3> -<p>The functions in the last section manipulate cells in the same way regardless of what data they contain. Other functions compare cells to each other, either testing whether they match or how they are ordered relative to one another. The two Grade functions <code><span class='Function'>⍋⍒</span></code>, and the self-comparison functions Classify (<code><span class='Function'>⊐</span></code>), Mark Firsts (<code><span class='Function'>∊</span></code>), and Occurrence Count (<code><span class='Function'>⊒</span></code>), each give a list result, with one number for each cell. We can see below that Occurrence Count returns the same results even as we make the argument cells more complicated, because the changes made preserve the matching of cells.</p> +<p>The functions in the last section manipulate cells in the same way regardless of what data they contain. Other functions compare cells to each other, either testing whether they match or how they are ordered relative to one another. The two <a href="order.html">Grade</a> functions <code><span class='Function'>⍋⍒</span></code>, and the <a href="selfcmp.html">self-comparison</a> functions Classify (<code><span class='Function'>⊐</span></code>), Mark Firsts (<code><span class='Function'>∊</span></code>), and Occurrence Count (<code><span class='Function'>⊒</span></code>), each give a list result, with one number for each cell. We can see below that Occurrence Count returns the same results even as we make the argument cells more complicated, because the changes made preserve the matching of cells.</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=cyDihpAgImFicmFjYWRhYnJhIgriipIgcwriipIg4omNy5ggcwriipIgcyDiiL7ijokw4oC/MSAic3VmZml4Ig==">↗️</a><pre> <span class='Value'>s</span> <span class='Gets'>←</span> <span class='String'>"abracadabra"</span> <span class='Function'>⊒</span> <span class='Value'>s</span> ⟨ 0 0 0 1 0 2 0 3 1 1 4 ⟩ @@ -107,13 +107,13 @@ ┘ </pre> <h3 id="other-monadic-functions">Other monadic functions</h3> -<p>Not all functions work on the first axis in a straightforward manner. <a href="transpose.html">Transpose</a> <code><span class='Function'>⍉</span></code> moves the first axis to the end, so while it focuses on the first one, it shifts every axis of the argument. <a href="join.html">Join</a> <code><span class='Function'>∾</span></code> also works on every axis of its argument, and applies to the leading axes of the argument's <em>elements</em> instead: these leading inner axes are matched up with the outer axes, and trailing inner axes are allowed but the elements must have rank at least as high as the argument array.</p> -<p>The other two monadic functions that work on high-rank arguments are Deshape (<code><span class='Function'>⥊</span></code>) and First (<code><span class='Function'>⊑</span></code>). These treat the argument as one long list, ordered by its element indices. This ordering privileges leading axes (in fact, it is the reason for the choice of leading axes in the leading axis convention), but these functions can't really be said to work on leading axes: they apply to all axes.</p> -<p>The Each (<code><span class='Modifier'>¨</span></code>) and Table (<code><span class='Modifier'>⌜</span></code>) modifiers return functions which are the same in the monadic case. These functions simply go through all elements of the argument array without regard for its multi-dimensional structure (the operand is applied to elements in index order, matching Deshape; this matters if it has side effects). Similarly, monadic arithmetic functions do not have any sort of leading axis dependence.</p> +<p>Not all functions work on the first axis in a straightforward manner. <a href="transpose.html">Transpose</a> <code><span class='Function'>⍉</span></code> moves the first axis to the end, so while it focuses on the first one, it shifts every axis of <code><span class='Value'>𝕩</span></code>. <a href="join.html">Join</a> <code><span class='Function'>∾</span></code> also works on every axis of its argument, and applies to the leading axes of <code><span class='Value'>𝕩</span></code>'s <em>elements</em> instead: these leading inner axes are matched up with the outer axes, and trailing inner axes are allowed but the elements must have rank at least as high as the argument array.</p> +<p>The other two monadic functions that work on high-rank arguments are <a href="reshape.html#deshape">Deshape</a> (<code><span class='Function'>⥊</span></code>) and <a href="pick.html#first">First</a> (<code><span class='Function'>⊑</span></code>). These treat <code><span class='Value'>𝕩</span></code> as one long list, ordered by its element indices. This ordering privileges leading axes (in fact, it is the reason for the choice of leading axes in the leading axis convention), but these functions can't really be said to work on leading axes: they apply to all axes.</p> +<p>The <a href="map.html">Each</a> (<code><span class='Modifier'>¨</span></code>) and <a href="map.html#table">Table</a> (<code><span class='Modifier'>⌜</span></code>) modifiers return functions which are the same in the monadic case. These functions simply go through all elements of the argument array without regard for its multi-dimensional structure (the operand is applied to elements in index order, matching Deshape; this matters if it has side effects). Similarly, monadic arithmetic functions do not have any sort of leading axis dependence.</p> <h2 id="dyadic-functions">Dyadic functions</h2> -<p>For dyadic functions the pattern of working on only one argument axis is not so common. Only two functions can be said to follow it roughly: Join to (<code><span class='Function'>∾</span></code>) combines two arrays along one axis, using the first axis of both arguments if they have the same rank and of the higher-rank argument if they differ by one. <a href="couple.html">Couple</a> (<code><span class='Function'>≍</span></code>), like Solo, does not manipulate the argument axes but adds a result axis. There are also some functions that can't be limited to leading axes: <a href="reshape.html">Reshape</a> (<code><span class='Function'>⥊</span></code>) treats the argument as one long list, and Pick (<code><span class='Function'>⊑</span></code>) requires each index to be as long as the right argument's rank, because it selects elements and not cells from the right argument.</p> +<p>For dyadic functions the pattern of working on only one argument axis is not so common. Only two functions can be said to follow it roughly: <a href="join.html">Join to</a> (<code><span class='Function'>∾</span></code>) combines two arrays along one axis, using the first axis of both arguments if they have the same rank and of the higher-rank argument if they differ by one. <a href="couple.html">Couple</a> (<code><span class='Function'>≍</span></code>), like Solo, does not manipulate the argument axes but adds a result axis. There are also some functions that can't be limited to leading axes: <a href="reshape.html">Reshape</a> (<code><span class='Function'>⥊</span></code>) treats <code><span class='Value'>𝕩</span></code> as one long list, and <a href="pick.html">Pick</a> (<code><span class='Function'>⊑</span></code>) requires each index to be as long as <code><span class='Value'>𝕩</span></code>'s rank, because it selects elements and not cells from <code><span class='Value'>𝕩</span></code>.</p> <h3 id="multiple-axes">Multiple axes</h3> -<p>Instead of always working on a single axis, many dyadic functions work on one axis by default, but also allow a left argument with multiple elements corresponding to leading axes of the right argument. To decide which of the two possibilities applies, these functions test the left argument depth, a convention that is discussed in the <a href="depth.html#testing-depth-for-multiple-axis-primitives">depth</a> documentation. A left argument that applies to one axis has a particular depth; the argument can also be a list of such arguments.</p> +<p>Instead of always working on a single axis, many dyadic functions work on one axis by default, but also allow a left argument with multiple elements corresponding to leading axes of <code><span class='Value'>𝕩</span></code>. To decide which of the two possibilities applies, these functions test the depth of <code><span class='Value'>𝕨</span></code>, a convention that is discussed in the <a href="depth.html#testing-depth-for-multiple-axis-primitives">depth</a> documentation. A left argument that applies to one axis has a particular depth; <code><span class='Value'>𝕨</span></code> can also be a list of such arguments.</p> <table> <thead> <tr> @@ -132,7 +132,7 @@ </tr> </tbody> </table> -<p>Functions such as Take and Drop use a single number per axis. When the left argument is a list of numbers, they apply to initial axes. Observing the operation of Rotate on the result of Range is instructive:</p> +<p>Functions such as Take and Drop use a single number per axis. When <code><span class='Value'>𝕨</span></code> is a list of numbers, they apply to initial axes. Observing the operation of <a href="reverse.html#rotate">Rotate</a> on the result of <a href="range.html">Range</a> is instructive:</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=MuKAvzEg4oy9IOKGlTPigL81">↗️</a><pre> <span class='Number'>2</span><span class='Ligature'>‿</span><span class='Number'>1</span> <span class='Function'>⌽</span> <span class='Function'>↕</span><span class='Number'>3</span><span class='Ligature'>‿</span><span class='Number'>5</span> ┌─ ╵ ⟨ 2 1 ⟩ ⟨ 2 2 ⟩ ⟨ 2 3 ⟩ ⟨ 2 4 ⟩ ⟨ 2 0 ⟩ @@ -140,13 +140,13 @@ ⟨ 1 1 ⟩ ⟨ 1 2 ⟩ ⟨ 1 3 ⟩ ⟨ 1 4 ⟩ ⟨ 1 0 ⟩ ┘ </pre> -<p>The array is shifted once to the left and twice upward, so that the first index (by ravel order) is now <code><span class='Function'>⊑</span><span class='Number'>2</span><span class='Ligature'>‿</span><span class='Number'>1</span><span class='Function'>⌽↕</span><span class='Number'>3</span><span class='Ligature'>‿</span><span class='Number'>5</span> <span class='Gets'>←→</span> <span class='Number'>2</span><span class='Ligature'>‿</span><span class='Number'>1</span></code>. To see how values are matched to leading axes, we can look at how Drop changes the shape of its argument:</p> +<p>The array is shifted once to the left and twice upward, so that the first index (by ravel order) is now <code><span class='Function'>⊑</span><span class='Number'>2</span><span class='Ligature'>‿</span><span class='Number'>1</span><span class='Function'>⌽↕</span><span class='Number'>3</span><span class='Ligature'>‿</span><span class='Number'>5</span> <span class='Gets'>←→</span> <span class='Number'>2</span><span class='Ligature'>‿</span><span class='Number'>1</span></code>. To see how values are matched to leading axes, we can look at how <a href="take.html">Drop</a> changes the shape of its argument:</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4omiIDPigL8yIOKGkyA34oC/N+KAvzfigL834qWKImFiYyI=">↗️</a><pre> <span class='Function'>≢</span> <span class='Number'>3</span><span class='Ligature'>‿</span><span class='Number'>2</span> <span class='Function'>↓</span> <span class='Number'>7</span><span class='Ligature'>‿</span><span class='Number'>7</span><span class='Ligature'>‿</span><span class='Number'>7</span><span class='Ligature'>‿</span><span class='Number'>7</span><span class='Function'>⥊</span><span class='String'>"abc"</span> ⟨ 4 5 7 7 ⟩ </pre> <p>Functions with single-axis depth 1 tend to be more complicated; see for example <a href="group.html#multidimensional-grouping">Group</a>.</p> <h3 id="leading-axis-agreement">Leading axis agreement</h3> -<p>Arithmetic functions, and the Each (<code><span class='Modifier'>¨</span></code>) and Depth (<code><span class='Modifier2'>⚇</span></code>) modifiers, use leading axis agreement to match their arguments together. All axes of the lower-rank argument are matched with the leading axes of the higher-rank one, and axes matched together must have the same length. After pairing axes in this way, a single element of the lower-rank argument might correspond to any number of elements of the higher-rank one. It's reused for each of those corresponding elements.</p> +<p><a href="arithmetic.html">Arithmetic</a> functions, and the <a href="map.html#each">Each</a> (<code><span class='Modifier'>¨</span></code>) and <a href="depth.html#the-depth-modifier">Depth</a> (<code><span class='Modifier2'>⚇</span></code>) modifiers, use leading axis agreement to match their arguments together. All axes of the lower-rank argument are matched with the leading axes of the higher-rank one, and axes matched together must have the same length. After pairing axes in this way, a single element of the lower-rank argument might correspond to any number of elements of the higher-rank one. It's reused for each of those corresponding elements.</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4oqiIHgg4oaQIDPigL8y4oC/NCDipYog4oaVNjAgICAgICMgQSByYW5rLTMgYXJyYXkKMTAw4oC/MOKAvzIwMCArIHggICAgICAgICAjIDAtY2VsbHMgcGFpcmVkIHdpdGggMi1jZWxscwriiqIgYyDihpAgMTAwIMOXIDMgPeKMnOKXi+KGlSAyICAjIEEgcmFuay0yIGFycmF5IHRvIGFkZApjICsgeCAgICAgICAgICAgICAgICAgIyAwLWNlbGxzIHBhaXJlZCB3aXRoIDEtY2VsbHMKeCArIHggICAgICAgICAgICAgICAgICMgUGFpcndpc2UgYWRkaXRpb24=">↗️</a><pre> <span class='Function'>⊢</span> <span class='Value'>x</span> <span class='Gets'>←</span> <span class='Number'>3</span><span class='Ligature'>‿</span><span class='Number'>2</span><span class='Ligature'>‿</span><span class='Number'>4</span> <span class='Function'>⥊</span> <span class='Function'>↕</span><span class='Number'>60</span> <span class='Comment'># A rank-3 array </span>┌─ ╎ 0 1 2 3 @@ -201,7 +201,7 @@ <p>If one argument is a unit, that is, it has no axes, then leading axis agreement reduces to APL's "scalar extension" (where "scalar" is equivalent to BQN's "unit"), where a single unit is matched with an entire array by repeating it at every application. A unit always agrees with any other array under leading axis agreement because it has no axes whose lengths would need to be checked.</p> <p>With leading axis agreement, there are <code><span class='Value'>k</span><span class='Function'>+</span><span class='Number'>1</span></code> shapes for arrays that can be added (or any other function with Each) to a given array <code><span class='Value'>x</span></code> without changing its rank. These are precisely the prefixes of <code><span class='Function'>≢</span><span class='Value'>x</span></code>, with ranks from <code><span class='Number'>0</span></code> to <code><span class='Value'>k</span></code> inclusive. Arrays with larger rank can also be used as the other argument, but then the result shape will match that argument and not <code><span class='Value'>x</span></code>.</p> <h3 id="search-functions">Search functions</h3> -<p>The search functions Bins (<code><span class='Function'>⍋⍒</span></code>), Index of (<code><span class='Function'>⊐</span></code>), Progressive Index of (<code><span class='Function'>⊒</span></code>), and Member of (<code><span class='Function'>∊</span></code>) look through cells of one argument to find cells of the other. Find (<code><span class='Function'>⍷</span></code>) also does a search, but a slightly different one: it tries to find <em>slices</em> of cells of its right argument that match the left argument.</p> +<p>The <a href="search.html">search functions</a> Bins (<code><span class='Function'>⍋⍒</span></code>), Index of (<code><span class='Function'>⊐</span></code>), Progressive Index of (<code><span class='Function'>⊒</span></code>), and Member of (<code><span class='Function'>∊</span></code>) look through cells of one argument to find cells of the other. Find (<code><span class='Function'>⍷</span></code>) also does a search, but a slightly different one: it tries to find <em>slices</em> of cells of <code><span class='Value'>𝕩</span></code> that match <code><span class='Value'>𝕨</span></code>.</p> <table> <thead> <tr> @@ -223,4 +223,4 @@ </tr> </tbody> </table> -<p>For all of these functions but Find, the argument to search through is treated as a list of its major cells. It is the rank of these major cells—let's call this rank <code><span class='Value'>c</span></code>—that determines how the other argument is treated. That argument must have rank at least <code><span class='Value'>c</span></code>, and it is treated as an array of <code><span class='Value'>c</span></code>-cells. For example, if the left argument to <code><span class='Function'>⍋</span></code> is a matrix, then each 1-cell or row of the right argument is treated independently, and each one yields one number in the result: a 0-cell. The result rank of <code><span class='Function'>⍋</span></code> is always <code><span class='Value'>𝕨</span><span class='Function'>¬</span><span class='Modifier2'>○</span><span class='Function'>=</span><span class='Value'>𝕩</span></code>.</p> +<p>For all of these functions but Find, the argument to search through is treated as a list of its major cells. It is the rank of these major cells—let's call this rank <code><span class='Value'>c</span></code>—that determines how the other argument is treated. That argument must have rank at least <code><span class='Value'>c</span></code>, and it is treated as an array of <code><span class='Value'>c</span></code>-cells. For example, if the left argument to <code><span class='Function'>⍋</span></code> is a matrix, then each 1-cell or row of <code><span class='Value'>𝕩</span></code> is treated independently, and each one yields one number in the result: a 0-cell. The result rank of <code><span class='Function'>⍋</span></code> is always <code><span class='Value'>𝕨</span><span class='Function'>¬</span><span class='Modifier2'>○</span><span class='Function'>=</span><span class='Value'>𝕩</span></code>.</p> |
