From 2010e8b2897a5741e211980c9f8ec9177299c939 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Fri, 16 Jul 2021 18:23:52 -0400 Subject: Finish links and editing documentation pass --- docs/doc/reverse.html | 10 +++++----- docs/doc/scan.html | 10 +++++----- docs/doc/select.html | 11 ++++++----- docs/doc/selfcmp.html | 12 ++++++------ docs/doc/shape.html | 6 +++--- docs/doc/shift.html | 34 ++++++++++++++++++++-------------- docs/doc/train.html | 14 +++++++------- docs/doc/transpose.html | 33 ++++++++++++++++++--------------- docs/doc/types.html | 10 +++++----- docs/doc/windows.html | 21 +++++++++++---------- 10 files changed, 86 insertions(+), 75 deletions(-) (limited to 'docs/doc') diff --git a/docs/doc/reverse.html b/docs/doc/reverse.html index edcb37a7..62eae902 100644 --- a/docs/doc/reverse.html +++ b/docs/doc/reverse.html @@ -5,7 +5,7 @@

Reverse and Rotate

-

The symbol indicates two different array transformations: with no left argument, it reverses the major cells of the array, but with a left argument, it rotates or cycles them around. These two possibilities, first put together in very early versions of APL, can't be considered restrictions or different views of some unifying function, but there are connections between them. Each returns an array with the same shape and all the same elements as 𝕩, possibly in a different arrangement. And elements that start out next to each other in 𝕩 generally stay next to each other—always, if we consider an element on one edge to be next to the one opposite to it. One might think of them as isometries preserving a discrete subgroup of the torus, if one were inclined to think such things. On major cells, the two functions decompose the dihedral group okay I'll stop.

+

The symbol indicates two different array transformations: with no left argument, it reverses the major cells of the array, but with a left argument, it rotates or cycles them around. These two possibilities, first put together in very early versions of APL, can't be considered restrictions or different views of some unifying function, but there are connections between them. Each returns an array with the same shape and all the same elements as 𝕩, possibly in a different arrangement. And elements that start out next to each other in 𝕩 generally stay next to each other—always, if we consider an element on one edge to be next to the one opposite to it. One might think of them as isometries preserving a discrete subgroup of the torus, if one were inclined to think such things. On major cells, the two functions decompose the dihedral group okay I'll stop.

Many uses of Rotate in APL are better handled by shift functions in BQN. If there's no reason to treat the data as cyclic or periodic, it's best to avoid Rotate.

Reverse

There's not too much to say about Reverse. It puts the elements of a list the other way around, or more generally the major cells of an array.

@@ -46,7 +46,7 @@ ⟨ 1 1 1 1 1 1 0 ⟩

Rotate

-

Rotate moves elements in a list around cyclically. It can also rotate any number of axes of the argument array by different amounts at once. That's discussed in the next section; for now we'll stick to a single number in the left argument. It has to be an integer, and the right argument has to be an array with at least one axis.

+

Rotate moves elements in a list around cyclically. It can also rotate any number of axes of the argument array by different amounts at once. That's discussed in the next section; for now we'll stick to a single number for 𝕨. It has to be an integer, and 𝕩 has to be an array with at least one axis.

↗️
    2  "rotate"
 "tatero"
 
@@ -64,12 +64,12 @@
     2  'c'  # No axes to rotate
 ERROR
 
-

Elements are always rotated to the left, so that entry i of the result is entry 𝕨+i of the argument—or rather, entry (𝕩)|𝕨+i to enable elements to cycle around. This can be seen directly by using n as an argument: in this case, the value of 𝕩 at index i is just i.

+

Elements are always rotated to the left, so that entry i of the result is entry 𝕨+i of the argument—or rather, entry (𝕩)|𝕨+i to enable elements to cycle around. This can be seen directly by using the range n as an argument: then the value of 𝕩 at index i is just i.

↗️
    2  6
 ⟨ 2 3 4 5 0 1 ⟩
 
-

The rotation (𝕩)𝕩 moves each element the entire length of 𝕩, which just places it back where it started. In fact, adding 𝕩 to the rotation amount never changes the behavior or the rotation. In terms of indices, this is because (𝕩)|(𝕩)+a is a.

-

To rotate the other way, use a negative left argument (so - is a simple way to write "reverse rotate"). This will always be the same as a leftwards rotation, since (-r)𝕩 is ((𝕩)-r)𝕩, but could be more convenient.

+

The rotation (𝕩)𝕩 moves each element the entire length of 𝕩, which just places it back where it started. In fact, adding 𝕩 to the rotation amount never changes the behavior or the rotation. In terms of indices, this is because (𝕩)|(𝕩)+a is a.

+

To rotate the other way, use a negative left argument (so - is a simple way to write "reverse rotate"). This will always be the same as some leftwards rotation, since (-r)𝕩 is ((𝕩)-r)𝕩, but could be more convenient.

↗️
    ¯2  "rotate"
 "terota"
 
diff --git a/docs/doc/scan.html b/docs/doc/scan.html index 1f572239..cc806de6 100644 --- a/docs/doc/scan.html +++ b/docs/doc/scan.html @@ -59,8 +59,8 @@

The 1-modifier Scan (`) moves along the first axis of the array 𝕩, building up an array of results by applying 𝔽 repeatedly beginning with 𝕨 or 𝕩. It's related to the fold modifiers, and most closely resembles the APL2-style reduction ¨˝, but it traverses the array in forward rather than reverse index order, and includes all intermediate results of 𝔽 in its output instead of just the final one.

-

BQN's Scan is ordered differently from Scan in APL. Both include one result for each non-empty prefix of 𝕩. In BQN this is a left-to-right fold, so that each new result requires one application of 𝔽. APL uses a right-to-left ordering, which matches with reduction, but requires starting over at the end for each new prefix, except in special cases. If needed, this definition can be obtained with a fold on each prefix except the first (which is empty). In the particular case of -, that nested solution isn't needed: negate odd-indexed elements and then apply +`.

-

Scan also differs from Fold or Insert in that it never depends on 𝔽's identity element, because scanning over an empty array simply returns that array.

+

BQN's Scan is ordered differently from Scan in APL. Both include one result for each non-empty prefix of 𝕩. In BQN this is a left-to-right fold, so that each new result requires one application of 𝔽. APL uses right-to-left folds, which matches with reduction, but requires starting over at the end for each new prefix, except in special cases. If needed, this definition can be obtained with a fold on each prefix except the first (which is empty). In the particular case of -, that nested solution isn't needed: negate odd-indexed elements and then apply +`.

+

Scan also differs from Fold or Insert in that it never depends on 𝔽's identity value, because scanning over an empty array simply returns that array.

Lists

The best-known use of Scan is the prefix sum of a list, in which each element of the result is the sum of that element and all the ones before it. With a shift this can be modified to sum the previous elements only.

↗️
    +` 2431
@@ -69,7 +69,7 @@
     +`»2431  # Exclusive prefix sum
 ⟨ 0 2 6 9 ⟩
 
-

The pattern is generalized to any function 𝔽. With an operand of ×, it can find the first n factorials. With , it returns the largest element so far.

+

The pattern is generalized to any function 𝔽. With an operand of ×, it can find the first n factorials. With Maximum (), it returns the largest element so far.

↗️
    ×` 1+↕6
 ⟨ 1 2 6 24 120 720 ⟩
 
@@ -94,7 +94,7 @@
     c
 9
 
-

Some other useful scans apply to boolean lists. The function ` tests whether this or any previous element is 1, so that the result starts at 0 but permanently switches to 1 as soon as the first 1 is found. Similarly, ` turns all instances of 1 after the first 0 to 0.

+

Some other useful scans apply to boolean lists. The function ` (with Or) tests whether this or any previous element is 1, so that the result starts at 0 but permanently switches to 1 as soon as the first 1 is found. Similarly, ` turns all instances of 1 after the first 0 to 0.

↗️
    ` 00100101
 ⟨ 0 0 1 1 1 1 1 1 ⟩
 
@@ -109,7 +109,7 @@
 "ab\rs\\"
 

Reverse scan

-

We've discussed how the scan moves forward along 𝕩, so that each time 𝔽 takes an old result as 𝕨 and a new value as 𝕩. This means that results correspond to prefixes and go left to right on each one. Since the most important scans have associative, commutative operands, the left-to-right ordering often doesn't make a difference. But sometimes a suffix rather than prefix scan is wanted. For these cases, Scan Under Reverse (`) does the trick.

+

We've discussed how the scan moves forward along 𝕩, so that each time 𝔽 takes an old result as 𝕨 and a new value as 𝕩. This means that results correspond to prefixes and go left to right on each one. Since the most important scans have associative, commutative operands, the left-to-right ordering often doesn't make a difference. But sometimes a suffix rather than prefix scan is wanted. For these cases, Scan Under Reverse (`) does the trick.

↗️
    `   0010010
 ⟨ 0 0 1 1 1 1 1 ⟩
 
diff --git a/docs/doc/select.html b/docs/doc/select.html
index 48f2af19..61a0f38d 100644
--- a/docs/doc/select.html
+++ b/docs/doc/select.html
@@ -48,7 +48,7 @@
   
 
 
-

The function Select () reorganizes the array 𝕩 along one or more axes based on indices given by 𝕨. The result has the same depth as 𝕩, since its elements are always elements of 𝕩. This means it differs from Pick (), which takes elements from 𝕩 but can arrange them in any nested structure, including returning an element directly.

+

The function Select () reorganizes the array 𝕩 along one or more axes based on indices given by 𝕨. The result has the same depth as 𝕩, since its elements are always elements of 𝕩. This means it differs from Pick (), which takes elements from 𝕩 but can arrange them in any nested structure, including returning an element directly.

The monadic form First Cell () gets the major cell with index 0, so that 𝕩 is identical to 0𝕩.

Single selection

Each axis of a BQN array is numbered starting at zero. Major cells are arranged along the first axis; in accordance with the leading axis principle, Select returns a major cell of 𝕩 when 𝕨 is an atom.

@@ -114,7 +114,8 @@ ERROR 0 1 4 9 5 3 3 ┘
-

More generally, 𝕨 can be an array of any rank. Each of its 0-cells—containing a single number—is replaced with a cell of 𝕩 in the result. The result's shape is then made up of the shape of 𝕨 and the major cell shape of 𝕩: it's (𝕨)1↓≢𝕩. When 𝕩 is a list, the result has the same shape as 𝕨. Elements of 𝕨 are replaced one-for-one with elements of 𝕩.

+

More generally, 𝕨 can be an array of any rank. Each of its 0-cells—containing a single number—is replaced with a cell of 𝕩 in the result. The result's shape is then made up of the shape of 𝕨 and the major cell shape of 𝕩: it's (𝕨)1↓≢𝕩.

+

When 𝕩 is a list, the result has the same shape as 𝕨. Elements of 𝕨 are replaced one-for-one with elements of 𝕩.

↗️
    2|m
 ┌─               
 ╵ 0 1 1 0 1 1 0  
@@ -168,8 +169,8 @@ ERROR
   ⟨ 1 3 ⟩ ⟨ 1 0 ⟩ ⟨ 1 0 ⟩  
                           ┘
 
-

Using a range for 𝕩 shows the structure of the selected elements more clearly, because each element is its own index. Each element of 𝕨 acts independently, giving a structure like the Table modifier.

-

While 𝕨 must have rank one or less, its elements can have any rank. When the elements are units, the corresponding axis of 𝕩 disappears from the result. We can select a 0-cell of 𝕩 in this way, although the more common case or selecting an element is handled by Pick.

+

Using a range for 𝕩 shows the structure of the selected elements more clearly, because each element is its own index. Each element of 𝕨 acts independently, giving a structure like the Table modifier.

+

While 𝕨 must have rank one or less, its elements can have any rank. When the elements are units, the corresponding axis of 𝕩 disappears from the result. We can select a 0-cell of 𝕩 in this way, although the more common case of selecting an element is handled by Pick.

↗️
    <4,<5,<1  (310)⥊↕1e3
 ┌·     
 · 451  
@@ -182,4 +183,4 @@ ERROR
 ⟨ 450 451 452 453 454 455 456 457 458 459 ⟩
 

If an element of 𝕨 has rank more than 1, it increases the rank of 𝕩 rather than decreasing it. The general rule is that in the result, one axis of 𝕩 is replaced by all the axes of the corresponding element of 𝕨 (trailing axes are unchanged). So the final shape 𝕨𝕩 is (∾≢¨𝕨)𝕨↓≢𝕩. But this shape doesn't affect the elements retrieved from 𝕩. In all cases, using ¨𝕨 for the left argument and then reshaping the result would yield the same value.

-

Selection only ever applies to leading axes of 𝕩. But you can skip some leading axes using ˘ or , to select on any contiguous set of axes. In particular, use the one-axis case 𝕨(-k) 𝕩 to select along axis k of 𝕩.

+

Selection only ever applies to leading axes of 𝕩. However, you can skip some leading axes using ˘ or , to select on any contiguous set of axes. In particular, use the one-axis case 𝕨(-k) 𝕩 to select along axis k of 𝕩.

diff --git a/docs/doc/selfcmp.html b/docs/doc/selfcmp.html index bc99ef43..8e9540f2 100644 --- a/docs/doc/selfcmp.html +++ b/docs/doc/selfcmp.html @@ -185,7 +185,7 @@ c ⟨ 0 1 2 ⟩ -

Applying both separately, in contrast, gives completely interesting results. These results contain all information from the original argument, as indicates which cells it contained and indicates where they were located. The function Select () reconstructs the argument from the two values.

+

Applying both separately, in contrast, gives completely interesting results. These results contain all information from the original argument, as indicates which cells it contained and indicates where they were located. The function Select () reconstructs the argument from the two values.

↗️
     c
 ┌─        
 ╵"yellow  
@@ -208,15 +208,15 @@
 

Applying both Classify and Deduplicate gives an array that has both properties (this isn't the case for all pairs of projections—we need to know that Classify maintains the uniqueness property for Deduplicate and vice-versa). It has no duplicate major cells, and it's a list of natural numbers that starts with 0 and never goes up by more than one. Taken together, these are a tight constraint! The first element of the argument has to be 0. The next can't be 0 because it's already appeared, but it can't be more than one higher—it has to be 1. The next can't be 0 or 1, and has to be 2. And so on. So the result is always n for some n. In fact it's possible to determine the length as well, by noting that each function preserves the number of unique major cells in its argument. Classify does this because distinct numbers in the output correspond exactly to distinct major cells in the input; Deduplicate does this because it only removes duplicate cells, not distinct ones. So the final result is n, where n is the number of unique major cells in the argument.

Mark Firsts

See the APL Wiki page on this function as well.

-

Mark Firsts () is the simplest self-search function: it returns 0 for any major cell of the argument that is a duplicate of an earlier cell and 1 for a major cell that's the first with its value. To implement Deduplicate in terms of Mark Firsts, just filter out the duplicates with /.

+

Mark Firsts () is the simplest self-search function: it returns 0 for any major cell of the argument that is a duplicate of an earlier cell and 1 for a major cell that's the first with its value. To implement Deduplicate in terms of Mark Firsts, just filter out the duplicates with /.

↗️
       314159265
 ⟨ 1 1 1 0 1 1 1 1 0 ⟩
 
     / 314159265
 ⟨ 3 1 4 5 9 2 6 ⟩
 
-

Mark Firsts has other uses, of course. Instead of keeping the unique values, you might remove the first of each value with ¬/. You can use ´ to check that an array has no duplicate major cells, or +´ to count the number of unique ones.

-

What about marking the elements that appear exactly once? There's a trick for this: find the cells that are firsts running both forwards () and backwards (). Such a cell has no equal before it, nor after it, so it's unique in the entire array.

+

Mark Firsts has other uses, of course. Instead of keeping the unique values, you might remove the first of each value with ¬/. You can use ´ to check that an array has no duplicate major cells, or +´ to count the number of unique ones.

+

What about marking the elements that appear exactly once? There's a trick for this: find the cells that are firsts running both forwards () and backwards (). Such a cell has no equal before it, nor after it, so it's unique in the entire array.

↗️
    (∊∧∊) "duck""duck""teal""duck""goose"
 ⟨ 0 0 1 0 1 ⟩
 
@@ -243,11 +243,11 @@ ↗️
    (1=⊒)/ "aaaabcddcc"
 "adc"
 
-

An interesting combination is Occurrence Count applied to the result of Indices (/). The result counts up to each number from the argument in turn; in other symbols, it's ∾↕¨. This version is interesting because it doesn't create any nested arrays, just lists of natural numbers.

+

An interesting combination is Occurrence Count applied to the result of Indices (/). The result counts up to each number from the argument in turn; in other symbols, it's ∾↕¨. This version is interesting because it doesn't create any nested arrays, just lists of natural numbers.

↗️
     / 234
 ⟨ 0 1 0 1 2 0 1 2 3 ⟩
 
-

A more efficient way when the interpreter doesn't optimize here is /(¯1⊑↕-⊏»)+`, but that's clearly quite a bit more complicated.

+

A more efficient way when doesn't have a fast implementation is /(¯1⊑↕-⊏»)+`, but that's clearly quite a bit more complicated.

Deduplicate

There's also an APL Wiki page on this function.

Deduplicate removes every major cell from the argument that matches an earlier cell, resulting in an array with the same rank but possibly a shorter length. It might also be described as returning the unique major cells of the argument, ordered by first occurrence. Deduplicate Under Reverse () orders by last occurrence instead.

diff --git a/docs/doc/shape.html b/docs/doc/shape.html index 5852a698..118353c6 100644 --- a/docs/doc/shape.html +++ b/docs/doc/shape.html @@ -8,7 +8,7 @@

The function Shape () returns an array's shape, and Rank (=) and Length () return properties that can be derived from the shape. BQN's arrays are multidimensional, so that the shape is a list of natural numbers (the length along each axis), while the rank (length of the shape) and length (of the first axis) are numbers. In these functions, an atom is treated as a unit array, which has rank 0 and empty shape. A unit has no first axis, but its length is defined to be 1.

Rank can be defined as while Length can be defined with a fold to be 1´.

Examples

-

The function Reshape () always returns an array of shape 𝕨, so we use it to make an array of shape 1326 in the example below (Take () shares this property).

+

The function Reshape () always returns an array of shape 𝕨, so we use it to make an array of shape 1326 in the example below (Take () shares this property if (𝕨)≤=𝕩).

↗️
     arr  1326  '0'+↕10
 ┌─        
 ┆"012345  
@@ -30,11 +30,11 @@
     = arr  # Rank
 4
 
-

The length is the first element of the shape, and the rank is the length of the shape—the number of axes. For another example, taking the first (and only) cell of arr gives an array with shape 326, length 3, and rank 3, as we can see by applying each function to arr.

+

The length is the first element of the shape, and the rank is the length of the shape—the number of axes. For another example, taking the first (and only) cell of arr gives an array with shape 326, length 3, and rank 3, as we can see by applying each function to arr.

↗️
    = {𝕎𝕩}¨< arr
 ⟨ ⟨ 3 2 6 ⟩ 3 3 ⟩
 
-

Applying Shape and the other two functions to an atom shows a shape of ⟨⟩, the empty list, and a rank of zero and length of 1. The same is true of an enclosed array, which like an atom is a kind of unit.

+

Applying Shape and the other two functions to an atom shows a shape of ⟨⟩ (the empty list), and a rank of zero and length of 1. The same is true of an enclosed array, which like an atom is a kind of unit.

↗️
     5
 ⟨⟩
 
diff --git a/docs/doc/shift.html b/docs/doc/shift.html
index 0d162721..c4b142c6 100644
--- a/docs/doc/shift.html
+++ b/docs/doc/shift.html
@@ -5,8 +5,8 @@
 
 
 

Shift functions

-

The shift functions « and » are two new primitives added to BQN based on a pattern used heavily in the compiler and a reasonable amount everywhere else. Shifts resemble but are more general than the bit-based shift operations used in low-level languages. They replace the APL pattern of a 2-wise reduction after appending or prepending an element (APL's 2≠/0,v translates to »v), one of the more common uses of 2-wise reduction.

-

The result of a shift function always has the same shape as its right argument. The function adds major cells to the beginning (») or end («) of 𝕩, moving the cells already in 𝕩 to accomodate them. Some cells on the opposite side from those added will "fall off" and not be included in the result.

+

The shift functions « and » add major cells to one side an array, displacing cells on the opposite side and moving those in between. Shifts resemble but are more general than the bit-based shift operations used in low-level languages. They replace the APL pattern of a 2-wise reduction after appending or prepending an element (APL's 2≠/0,v translates to »v), one of the more common uses of 2-wise reduction.

+

The result of a shift function always has the same shape as 𝕩. The function adds major cells to the beginning (») or end («) of 𝕩, moving the cells already in 𝕩 to accomodate them. Some cells on the opposite side from those added will "fall off" and not be included in the result.

↗️
    00 » 321             # Shift Before
 ⟨ 0 0 3 ⟩
     "end" « "add to the "   # Shift After
@@ -20,7 +20,7 @@
 

If 𝕨 is longer than 𝕩, some cells from 𝕨 will be discarded, as well as all of 𝕩. In this case 𝕨»𝕩 is (𝕩)𝕨 and 𝕨«𝕩 is (-≠𝕩)𝕨. For similar reasons, nudging an array of length 0 returns it unchanged.

Sequence processing with shifts

-

When working with a sequence of data such as text, daily measurements, or audio data, shift functions are generally the best way to handle the concept of "next" or "previous" in the data. In the following example s is shown alongside the shifted-right data »s, and each element is compared to the previous with -», which we see is the inverse of +`.

+

When working with a sequence of data such as text, daily measurements, or audio data, shift functions are generally the best way to handle the concept of "next" or "previous". In the following example s is shown alongside the shifted-right data »s, and each element is compared to the previous with -», which we see is the inverse of Plus Scan +`.

↗️
    s  1224356
     s  »s
 ┌─               
@@ -34,8 +34,9 @@
 ⟨ 1 2 2 4 3 5 6 ⟩
 

In this way » refers to a sequence containing the previous element at each position. By default the array's fill is used for the element before the first, and a right argument can be given to provide a different one.

-↗️
     » s
+↗️
     » s
 ⟨ ∞ 1 2 2 4 3 5 ⟩
+
     » s
 ⟨ 1 1 2 2 4 3 5 ⟩
 
@@ -76,17 +77,19 @@ ⟨ 1 1 1 1 0 0 1 1 ⟩

Other examples

-

In Take (), there's no way to specify the fill element when the result is longer than the argument. To take along the first axis with a specified, constant fill value, you can use Shift Before instead, where the right argument is an array of fills with the desired final shape.

+

In Take (), there's no way to specify the fill element when the result is longer than the argument. To take along the first axis with a specified, constant fill value, you can use Shift Before instead, where the right argument is an array of fills with the desired final shape.

↗️
    "abc" » 5'F'
 "abcFF"
 
-

When using Scan (`), the result at a particular index is obtained from values up to and including the one at that index. But it's common to want to use the values up to but not including that one instead. This can be done either by joining or shifting in that value before scanning. The difference is that with Join the result is longer than the argument. Either form might be wanted depending on how it will be used.

-↗️
    +` 1111
-⟨ 1 2 3 4 ⟩
-    2 +` 1111
-⟨ 2 3 4 5 6 ⟩
-    2 +`» 1111
-⟨ 2 3 4 5 ⟩
+

When using Scan (`), the result at a particular index is obtained from values up to and including the one at that index. But it's common to want to use the values up to but not including that one instead. This can be done either by joining or shifting in that value before scanning. The difference is that with Join the result is longer than the argument. Either form might be wanted depending on how it will be used.

+↗️
    2 +` 1010    # Initial value not retained
+⟨ 3 3 4 4 ⟩
+
+    2 +` 1010  # All values
+⟨ 2 3 3 4 4 ⟩
+
+    2 +`» 1010  # Final value not created
+⟨ 2 3 3 4 ⟩
 

The strides of an array are the distances between one element and the next along any given axis. It's the product of all axis lengths below that axis, since these are all the axes that have to be "skipped" to jump along the axis. The strides of an array 𝕩 are (×`1»⊢)𝕩.

↗️
    (×`1»⊢) 5243
@@ -94,13 +97,14 @@
 

Higher rank

Shifting always works on the first axis of 𝕩 (which must have rank 1 or more), and shifts in major cells. A left argument can have rank equal to 𝕩, or one less than it, in which case it becomes a single cell of the result. With no left argument, a cell of fills 10𝕩 is nudged in.

-↗️
     a  (↕×´) 43
+↗️
     a  (↕×´) 43
 ┌─         
 ╵ 0  1  2  
   3  4  5  
   6  7  8  
   9 10 11  
           ┘
+
     » a                # Nudge adds a cell of fills
 ┌─       
 ╵ 0 0 0  
@@ -108,6 +112,7 @@
   3 4 5  
   6 7 8  
         ┘
+
     "one" « a          # Shift in a cell
 ┌─             
 ╵ 3   4   5    
@@ -115,6 +120,7 @@
   9   10  11   
   'o' 'n' 'e'  
               ┘
+
     ("two""cel") « a  # Shift in multiple cells
 ┌─             
 ╵ 6   7   8    
@@ -125,5 +131,5 @@
 

Definition

In any instance of » or «, 𝕩 must have rank at least 1.

-

For a dyadic shift function, 𝕨 must be Join-compatible with 𝕩 (that is, 𝕨𝕩 completes without error) and cannot have greater rank than 𝕩. Then Shift Before (») is {(𝕩)𝕨𝕩} and Shift After («) is {(-≠𝕩)𝕩𝕨}

+

For a dyadic shift function, 𝕨 must be Join-compatible with 𝕩 (that is, 𝕨𝕩 completes without error) and cannot have greater rank than 𝕩. Then Shift Before (») is {(𝕩)𝕨𝕩} and Shift After («) is {(-≠𝕩)𝕩𝕨}

When called monadically, the default argument is a cell of fills 10𝕩. That is, Nudge (») is (10↑⊢)» and Nudge Back («) is (10↑⊢)«. This default argument always satisfies the compatibility requirement above and so the only conditions for nudge are that 𝕩 has rank at least 1 and has a fill element.

diff --git a/docs/doc/train.html b/docs/doc/train.html index d55bb290..8800d7f5 100644 --- a/docs/doc/train.html +++ b/docs/doc/train.html @@ -5,7 +5,7 @@

Function trains

-

Trains are an important aspect of BQN's tacit programming capabilities. In fact, a crucial one: with trains and the identity functions Left () and Right (), a fully tacit program can express any explicit function whose body is a statement with 𝕨 and 𝕩 used only as arguments (that is, there are no assignments and 𝕨 and 𝕩 are not used in operands or lists. Functions with assignments may have too many variables active at once to be directly translated but can be emulated by constructing lists. But it's probably a bad idea). Without trains it isn't possible to have two different functions that each use both arguments to a dyadic function. With trains it's perfectly natural.

+

Trains are an important aspect of BQN's tacit programming capabilities. In fact, a crucial one: with trains and the identity functions Left () and Right (), a fully tacit program can express any explicit function whose body is a statement with 𝕨 and 𝕩 used only as arguments (that is, there are no assignments and 𝕨 and 𝕩 are not used in operands or lists. Functions with assignments may have too many variables active at once to be directly translated but can be emulated by constructing lists. But it's probably a bad idea). Without trains it isn't possible to have two different functions that each use both arguments to a dyadic function. With trains it's perfectly natural.

BQN's trains are the same as those of Dyalog APL, except that Dyalog is missing the minor convenience of BQN's Nothing (·). There are many Dyalog-based documents and videos on trains you can view on the APL Wiki.

2-train, 3-train

Trains are an adaptation of the mathematical convention that, for example, two functions F and G can be added to get a new function F+G that applies as (F+G)(x) = F(x)+G(x). With a little change to the syntax, we can do exactly this in BQN:

@@ -24,7 +24,7 @@ "ab""cde""f" "fcdeab"
-

The three functions ∾⌽, ·∾⌽, and are completely identical. Why might we want three different ways to write the same thing? If we only want to define a function, there's hardly any difference. However, these three forms have different syntax, and might be easier or harder to use in different contexts. As we'll see, we can use inside a train without parenthesizing it, and string ·∾⌽ but not ∾⌽ together with other trains. Let's look at how the train syntax extends to longer expressions.

+

The three functions ∾⌽, ·∾⌽, and are completely identical: Join of Reverse. Why might we want three different ways to write the same thing? If we only want to define a function, there's hardly any difference. However, these three forms have different syntax, and might be easier or harder to use in different contexts. As we'll see, we can use inside a train without parenthesizing it, and string ·∾⌽ but not ∾⌽ together with other trains. Let's look at how the train syntax extends to longer expressions.

Longer trains

Function application in trains, as in other contexts, shares the lowest precedence level with assignment. Modifiers and strands (with ) have higher precedence, so they are applied before forming any trains. Once this is done, an expression is a subject expression if it ends with a subject and a function expression if it ends with a function (there are also modifier expressions, which aren't relevant here). A train is any function expression with multiple functions or subjects in it: while we've seen examples with two or three functions, any number are allowed.

Subject expressions are the domain of "old-school" APL, and just apply one function after another to a subject, possibly assigning some of the results (that's the top-level picture—anything can still happen within parentheses). Subjects other than the first appear only as left arguments to functions, which means that two subjects can't appear next to each other because the one on the left would have no corresponding function. Here's an example from the compiler (at one point), with functions and assignments numbered in the order they are applied and their arguments marked with «», and a fully-parenthesized version shown below.

@@ -41,24 +41,24 @@

In a train, arguments alternate strictly with combining functions between them. Arguments can be either functions or subjects, except for the rightmost one, which has to be a function to indicate that the expression is a train. Trains tend to be shorter than subject expressions partly because to keep track of this alternation in a train of all functions, you need to know where each function is relative to the end of the train (subjects like the ¯1 above only occur as left arguments, so they can also serve as anchors).

Practice training

-

The train ⊢>¯1»⌈` is actually a nice trick for marking first occurrences 𝕩 given the self-classify 𝕩 without doing another search. Let's take a closer look, first by applying it mechanically. To do this, we apply each "argument" to the train's argument, and then combine them with the combining functions.

+

The train ⊢>¯1»⌈` is actually a nice trick to get the result of Mark Firsts 𝕩 given the result of Classify 𝕩, without doing another search. Let's take a closer look, first by applying it mechanically. To do this, we apply each "argument" to the train's argument, and then combine them with the combining functions.

( > ¯1 » `) 𝕩
 (𝕩) > (¯1) » (`𝕩)
 𝕩 > ¯1 » `𝕩
 

So—although not all trains simplify so much—this confusing train is just {𝕩>¯1»⌈`𝕩}! Why would I write it in such an obtuse way? To someone used to working with trains, the function (⊢>¯1»⌈`) isn't any more complicated to read: in an argument position of a train just means 𝕩 while ` will be applied to the arguments. Using the train just means slightly shorter code and two fewer 𝕩s to trip over.

-

This function's argument is the self-classify of some list (in fact this technique also works on the self-indices 𝕩𝕩). Self-classify moves along its argument, giving each major cell a number: the first unused natural number if that value hasn't been seen yet, and otherwise the number chosen when it was first seen. It can be implemented as ∊⊐⊢, another train!

+

This function's argument is Classify () of some list (in fact this technique also works on the index-of-self 𝕩𝕩). Classify moves along its argument, giving each major cell a number: the first unused natural number if that value hasn't been seen yet, and otherwise the number chosen when it was first seen. It can be implemented as ⍷⊐⊢, another train!

↗️
     sc   "tacittrains"
 ⟨ 0 1 2 3 0 0 4 1 3 5 6 ⟩
 
-

Each 't' is 0, each 'a' is 1, and so on. We'd like to discard some of the information in the self-classify, to just find whether each major cell had a new value. Here are the input and desired result:

+

Each 't' is 0, each 'a' is 1, and so on. We'd like to discard some of the information from Classify, to just find whether each major cell had a new value. Here are the input and desired result:

↗️
    sc   "tacittrains"
 ┌─                       
 ╵ 0 1 2 3 0 0 4 1 3 5 6  
   1 1 1 1 0 0 1 0 0 1 1  
                         ┘
 
-

The result should be 1 when a new number appears, higher than all the previous numbers. To do this, we first find the highest previous number by taking the maximum-scan ` of the argument, then shifting to move the previous maximum to the current position. The first cell is always new, so we shift in a ¯1, so it will be less than any element of the argument.

+

The result should be 1 when a new number appears, higher than all the previous numbers. To do this, we first find the highest previous number by taking the maximum-scan ` of the argument, then shifting to move the previous maximum to the current position. The first cell is always new, so we shift in a ¯1, so it will be less than any element of the argument.

↗️
    ¯1 » `sc
 ⟨ ¯1 0 1 2 3 3 3 4 4 4 5 ⟩
     (¯1»⌈`) sc
@@ -72,7 +72,7 @@
 

Composing trains

The example above uses a train with five functions: an odd number. Trains with an odd length are always composed of length-3 trains, and they themselves are composed the same way as subject expressions: an odd-length train can be placed in the last position of another train without parentheses, but it needs parentheses to go in any other position.

-

But we also saw the length-2 train ∾⌽ above. Even-length trains consist of a single function () applied to a function or odd-length train (); another perspective is that an even-length train is an odd-length train where the left argument of the final (leftmost) function is left out, so it's called with only a right argument. An even-length train always needs parentheses if it's used as one of the functions in another train. However, it can also be turned into an odd-length train by placing · at the left, making the implicit missing argument explicit. After this it can be used at the end of an odd-length train without parentheses. To get some intuition for even-length trains, let's look at an example of three functions used together: the unique () sorted () absolute values (|) of an argument list.

+

But we also saw the length-2 train ∾⌽ above. Even-length trains consist of a single function () applied to a function or odd-length train (); another perspective is that an even-length train is an odd-length train where the left argument of the final (leftmost) function is left out, so it's called with only a right argument. An even-length train always needs parentheses if it's used as one of the functions in another train. However, it can also be turned into an odd-length train by placing · at the left, making the implicit missing argument explicit. After this it can be used at the end of an odd-length train without parentheses. To get some intuition for even-length trains, let's look at an example of three functions used together: the unique () sorted () absolute values (|) of an argument list.

↗️
    ⍷∧| 34¯3¯20
 ⟨ 0 2 3 4 ⟩
 
diff --git a/docs/doc/transpose.html b/docs/doc/transpose.html index f86029a7..ee3e926c 100644 --- a/docs/doc/transpose.html +++ b/docs/doc/transpose.html @@ -5,7 +5,7 @@

Transpose

-

As in APL, Transpose () is a tool for rearranging the axes of an array. BQN's version is tweaked to align better with the leading axis model and make common operations easier.

+

Transpose () is a tool for rearranging the axes of an array. BQN's version is tweaked relative to APL to align better with the leading axis model and make common operations easier.

Transpose basics

The name for the primitive comes from the Transpose operation on matrices. Given a matrix as an array of rank 2, will transpose it:

↗️
     mat  23  6
@@ -29,13 +29,14 @@
 

With two axes the only interesting operation of this sort is to swap them (and with one or zero axes there's nothing interesting to do, and just returns the argument array). But a BQN programmer may well want to work with higher-rank arrays—although such a programmer might call them "tensors"—and this means there are many more ways to rearrange the axes. Transpose extends to high-rank arrays to allow some useful special cases as well as completely general axis rearrangement, as described below.

Monadic Transpose

APL extends matrix transposition to any rank by reversing all axes for its monadic , but this generalization isn't very natural and is almost never used. The main reason for it is to maintain the equivalence a MP b ←→ b MP a, where MP +˝×1 is the generalized matrix product. But even here APL's Transpose is suspect. It does much more work than it needs to, as we'll see.

-

BQN's transpose takes the first axis of its argument and moves it to the end.

-↗️
     a23456  23456
+

BQN's transpose takes the first axis of 𝕩 and moves it to the end.

+↗️
     a23456  23456
 ⟨ 2 3 4 5 6 ⟩
+
       a23456
 ⟨ 3 4 5 6 2 ⟩
 
-

In terms of the argument data as given by Deshape (), this looks like a simple 2-dimensional transpose: one axis is exchanged with a compound axis made up of the other axes. Here we transpose a rank 3 matrix:

+

In terms of the argument data as given by Deshape (), this looks like a simple 2-dimensional transpose: one axis is exchanged with a compound axis made up of the other axes. Here we transpose a rank 3 matrix:

↗️
    a322  322⥊↕12
     < a322
 ┌─                      
@@ -62,9 +63,10 @@
                          ┘  
                            ┘
 
-

To exchange multiple axes, use the Power modifier. A negative power moves axes in the other direction, just like how Rotate handles negative left arguments. In particular, to move the last axis to the front, use Undo (as you might expect, this exactly inverts ).

-↗️
     3 a23456
+

To exchange multiple axes, use the Repeat modifier. A negative power moves axes in the other direction, just like how Rotate handles negative left arguments. In particular, to move the last axis to the front, use Undo (as you might expect, this exactly inverts ).

+↗️
     3 a23456
 ⟨ 5 6 2 3 4 ⟩
+
       a23456
 ⟨ 6 2 3 4 5 ⟩
 
@@ -73,11 +75,11 @@ ↗️
     3 a23456
 ⟨ 2 3 5 6 4 ⟩
 
-

And of course, Rank and Power can be combined to do more complicated transpositions: move a set of contiguous axes with any starting point and length to the end.

+

And of course, Rank and Repeat can be combined to do more complicated transpositions: move a set of contiguous axes with any starting point and length to the end.

↗️
     ¯1 a23456
 ⟨ 2 6 3 4 5 ⟩
 
-

Using these forms, we can state BQN's generalized matrix product swapping rule:

+

Using these forms (and the Rank function), we can state BQN's generalized matrix product swapping rule:

a MP b  ←→  (1-=a) (b) MP (a)
 

Certainly not as concise as APL's version, but not a horror either. BQN's rule is actually more parsimonious in that it only performs the axis exchanges necessary for the computation: it moves the two axes that will be paired with the matrix product into place before the product, and directly exchanges all axes afterwards. Each of these steps is equivalent in terms of data movement to a matrix transpose, the simplest nontrivial transpose to perform. Also remember that for two-dimensional matrices both kinds of transposition are the same, so that APL's simpler rule MP MP˜ holds in BQN.

@@ -87,9 +89,10 @@

In a case like this BQN's Dyadic transpose is much easier.

Dyadic Transpose

-

Transpose also allows a left argument that specifies a permutation of the right argument's axes. For each index pi𝕨 in the left argument, axis i of the argument is used for axis p of the result. Multiple argument axes can be sent to the same result axis, in which case that axis goes along a diagonal of the argument array, and the result will have a lower rank than the argument.

-↗️
     13204  a23456
+

Transpose also allows a left argument that specifies a permutation of 𝕩's axes. For each index pi𝕨 in the left argument, axis i of 𝕩 is used for axis p of the result. Multiple argument axes can be sent to the same result axis, in which case that axis goes along a diagonal of 𝕩, and the result will have a lower rank than 𝕩.

+↗️
     13204  a23456
 ⟨ 5 2 4 3 6 ⟩
+
      12200  a23456  # Don't worry too much about this case though
 ⟨ 5 2 3 ⟩
 
@@ -97,17 +100,17 @@ ↗️
     13204  a23456
 ⟨ 3 5 4 2 6 ⟩
 
-

So far, all like APL. BQN makes one little extension, which is to allow only some axes to be specified. The left argument will be matched up with leading axes of the right argument. Those axes are moved according to the left argument, and remaining axes are placed in order into the gaps between them.

+

So far, all like APL. BQN makes one little extension, which is to allow only some axes to be specified. Then 𝕨 will be matched up with leading axes of 𝕩. Those axes are moved according to 𝕨, and remaining axes are placed in order into the gaps between them.

↗️
     024  a23456
 ⟨ 2 5 3 6 4 ⟩
 
-

In particular, the case with only one argument specified is interesting. Here, the first axis ends up at the given location. This gives us a much better solution to the problem at the end of the last section.

+

In particular, the case with only one axis specified is interesting. Here, the first axis ends up at the given location. This gives us a much better solution to the problem at the end of the last section.

↗️
     2  a23456  # Restrict Transpose to the first three axes
 ⟨ 3 4 2 5 6 ⟩
 

Finally, it's worth noting that, as monadic Transpose moves the first axis to the end, it's equivalent to dyadic Transpose with a "default" left argument: (=-1˙).

Definitions

Here we define the two valences of Transpose more precisely.

-

Monadic transpose is identical to (=-1˙), except that if the argument is a unit it is returned unchanged rather than giving an error.

-

An atom right argument to dyadic Transpose is always enclosed to get an array before doing anything else.

-

In dyadic Transpose, the left argument is a number or numeric array of rank 1 or less, and 𝕨≠≢𝕩. Define the result rank r(=𝕩)-+´¬∊𝕨 to be the argument rank minus the number of duplicate entries in the left argument. We require ´𝕨<r. Bring 𝕨 to full length by appending the missing indices: 𝕨𝕨(¬˜/⊢)r. Now the result shape is defined to be ´¨𝕨⊔≢𝕩. Element iz of the result z is element (𝕨i)𝕩 of the argument.

+

An atom right argument to either valence of Transpose is always enclosed to get an array before doing anything else.

+

Monadic transpose is identical to (=-1˙), except that if 𝕩 is a unit it is returned unchanged (after enclosing, if it's an atom) rather than giving an error.

+

In dyadic Transpose, 𝕨 is a number or numeric array of rank 1 or less, and 𝕨≠≢𝕩. Define the result rank r(=𝕩)-+´¬∊𝕨 to be the right argument rank minus the number of duplicate entries in the left argument. We require ´𝕨<r. Bring 𝕨 to full length by appending the missing indices: 𝕨𝕨(¬˜/⊢)r. Now the result shape is defined to be ´¨𝕨⊔≢𝕩. Element iz of the result z is element (𝕨i)𝕩 of the argument.

diff --git a/docs/doc/types.html b/docs/doc/types.html index f228bd0f..297989c1 100644 --- a/docs/doc/types.html +++ b/docs/doc/types.html @@ -42,23 +42,23 @@ -

The reason operations and namespaces are called "mutable" is that the values obtained from them—by calling an operation on particular arguments or reading a field from a namespace—may change over the course of the program. This property is caused by variable modification , which can directly change a namespace field, or change the behavior of an operation that uses the modified variable. This means that a program that doesn't include won't have such changes in behavior. However, there will still be an observable difference between immutable data and the mutable types: code that creates a mutable value (for example, a block function {𝕩}) creates a different one each time, so that two different instances don't match () each other. Data values created at different times may match, but mutable values never will.

+

The reason operations and namespaces are called "mutable" is that the values obtained from them—by calling an operation on particular arguments or reading a field from a namespace—may change over the course of the program. This property is caused by variable modification , which can directly change a namespace field, or change the behavior of an operation that uses the modified variable. This means that a program that doesn't include won't have such changes in behavior. However, there will still be an observable difference between immutable data and the mutable types: code that creates a mutable value (for example, a block function {𝕩}) creates a different one each time, so that two different instances don't match () each other. Data values created at different times may match, but mutable values never will.

An array is considered immutable because its shape, and what elements it contains, cannot change. An array has no identity outside these properties (and possibly its fill element), so an array with a different shape or different elements would simply be a different array. However, any element of an array could be mutable, in which case the behavior of the array would change with respect to the operation of selecting that element and calling it or accessing a field.

Data types

-

Data types—numbers, characters, and arrays—are more like "things" than "actions". If called as a function, a value of one of these types simply returns itself. Data can be uniquely represented, compared for equality, and ordered using BQN's array ordering; in contrast, determining whether two functions always return the same result can be undecidable. For arrays, these properties apply only if there are no operations inside. We might say that "data" in BQN refers to numbers, characters, and arrays of data.

+

Data types—numbers, characters, and arrays—are more like "things" than "actions". If called as a function, a value of one of these types simply returns itself. Data can be uniquely represented, compared for equality, and ordered using BQN's array ordering; in contrast, determining whether two functions always return the same result can be undecidable. For arrays, these properties apply only if there are no operations inside. We might say that "data" in BQN refers to numbers, characters, and arrays of data.

Numbers

The BQN spec allows for different numeric models to be used, but requires there to be only one numeric type from the programmer's perspective: while programs can often be executed faster by using limited-range integer types, there is no need to expose these details to the programmer. Existing BQN implementations are based on double-precision floats, like Javascript or Lua.

Characters

A character in BQN is always a Unicode code point. BQN does not use encodings such as UTF-8 or UTF-16 for characters, although it would be possible to store arrays of integers or characters that correspond to data in these encodings. Because every code point corresponds to a single unit in UTF-32, BQN characters can be thought of as UTF-32 encoded.

-

Addition and subtraction treat characters as an affine space relative to the linear space of numbers. This means that:

+

Addition and subtraction treat characters as an affine space relative to the linear space of numbers. This means that:

  • A number can be added to or subtracted from a character.
  • Two characters can be subtracted to get the distance between them—a number.

Other linear combinations such as adding two characters or negating a character are not allowed. You can check whether an application of + or - on numbers and characters is allowed by applying the same function to the "characterness" of each value: 0 for a number and 1 for a character. The result will be a number if this application gives 0 and a character if this gives 1, and otherwise the operation is not allowed.

Arrays

-

A BQN array is a multidimensional arrangement of data. This means it has a certain shape, which is a finite list of natural numbers giving the length along each axis, and it contains an element for each possible index, which is a choice of one natural number that's less than each axis length in the shape. The total number of elements, or bound, is then the product of all the lengths in the shape. The shape may have any length including zero, and this shape is known as the array's rank. An array of rank 0, which always contains exactly one element, is called a unit, while an array of rank 1 is called a list and an array of rank 2 is called a table.

-

Each array—empty or nonempty—has an inferred property called a fill. The fill either indicates what element should be used to pad an array, or that such an element is not known and an error should result. Fills are used by Take () and the two Nudge functions (»«).

+

A BQN array is a multidimensional arrangement of data. This means it has a certain shape, which is a finite list of natural numbers giving the length along each axis, and it contains an element for each possible index, which is a choice of one natural number that's less than each axis length in the shape. The total number of elements, or bound, is then the product of all the lengths in the shape. The shape may have any length including zero, and this shape is known as the array's rank. An array of rank 0, which always contains exactly one element, is called a unit, while an array of rank 1 is called a list and an array of rank 2 is called a table.

+

Each array—empty or nonempty—has an inferred property called a fill. The fill either indicates what element should be used to pad an array, or that such an element is not known and an error should result. Fills can be used by Take (), the two Nudge functions (»«), First (), and Reshape ().

Arrays are value types (or immutable), so that there is no way to "change" the shape or elements of an array. An array with different properties is a different array. As a consequence, arrays are an inductive type, and it's not possible for an array to contain itself, or contain an array that contains itself, and so on. However, it is possible for an array to contain a function or other operation that has access to the array through a variable, and in this sense an array can "know about" itself.

Different elements of an array should not influence each other. While some APLs force numbers placed in the same array to a common representation, which may have different precision properties, BQN values must not change behavior when placed in an array. However, this doesn't preclude changing the storage type of an array for better performance: for example, in a BQN implementation using 64-bit floats, an array whose elements are all integers that fit in 32-bit int range might be represented as an array of 32-bit ints.

Operation types

diff --git a/docs/doc/windows.html b/docs/doc/windows.html index dc84bc73..bbfc50c3 100644 --- a/docs/doc/windows.html +++ b/docs/doc/windows.html @@ -16,8 +16,8 @@ cdefg" ┘
-

There are 1+(𝕩)-𝕨, or (𝕩)¬𝕨, of these sections, because the starting index must be at least 0 and at most (𝕩)-𝕨. Another way to find this result is to look at the number of cells in or before a given slice: there are always 𝕨 in the slice and there are only 𝕩 in total, so the number of slices is the range spanned by these two endpoints.

-

You can take a slice of an array 𝕩 that has length l and starts at index i using li𝕩 or li𝕩. The Prefixes function returns all the slices that end at the end of the array ((𝕩)=i+l), and Suffixes gives the slices that start at the beginning (i=0). Windows gives yet another collection of slices: the ones that have a fixed length l=𝕨. Selecting one cell from its result gives you the slice starting at that cell's index:

+

There are 1+(𝕩)-𝕨, or (𝕩)¬𝕨, of these sections, because the starting index must be at least 0 and at most (𝕩)-𝕨. Another way to find this result is to look at the number of cells in or before a given slice: there are always 𝕨 in the slice and there are only 𝕩 in total, so the number of slices is the range spanned by these two endpoints.

+

You can take a slice of an array 𝕩 that has length l and starts at index i using Take with Drop or Rotate: li𝕩 or li𝕩. The Prefixes function returns all the slices that end at the end of the array ((𝕩)=i+l), and Suffixes gives the slices that start at the beginning (i=0). Windows gives yet another collection of slices: the ones that have a fixed length l=𝕨. Selecting one cell from its result gives you the slice starting at that cell's index:

↗️
    25"abcdefg"
 "cdefg"
     52"abcdefg"
@@ -48,12 +48,12 @@
                        ┘
 

The slices are naturally arranged along multiple dimensions according to their starting index. Once again the equivalence ilx ←→ lix holds, provided i and l have the same length.

-

If the left argument has length 0, then the argument is not sliced along any dimensions. The only slice that results—the entire argument—is then arranged along an additional zero dimensions. In the end, the result is the same as the argument.

+

If 𝕨 has length 0, then 𝕩 is not sliced along any dimensions. The only slice that results—the entire argument—is then arranged along an additional zero dimensions. In the end, the result is 𝕩, unchanged.

More formally

𝕩 is an array. 𝕨 is a number, or numeric list or unit, with 𝕨≠≢𝕩. The result z has shape 𝕨∾¬𝕨((𝕨))𝕩, and element iz is 𝕩˜(𝕨)(↑+((𝕨)))i.

Using Group we could also write iz ←→ 𝕩˜(𝕨()𝕩) +´¨ i.

Symmetry

-

Let's look at an earlier example, along with its transpose.

+

Let's look at an earlier example, along with its Transpose ().

↗️
    {𝕩,𝕩}5"abcdefg"
 ┌─                   
 · ┌─        ┌─       
@@ -73,22 +73,23 @@
 ↗️
    {(5𝕩)≡⍉(3𝕩)}"abcdefg"
 1
 
-

In general, we need a more complicated transpose—swapping the first set of 𝕨 axes with the second set. Note again the use of Span, our slice-length to slice-number converter.

+

In general, we need a more complicated transpose—swapping the first set of 𝕨 axes with the second set. Note again the use of Span, our slice-length to slice-number converter.

↗️
    {((56¬22)𝕩)  23(22𝕩)} 567
 1
 

Applications

-

Windows can be followed up with a reduction on each slice to give a windowed reduction. Here we take running sums of 3 values.

-↗️
    +´˘3 2,6,0,1,4,3
+

Windows can be followed up with a reduction on each slice to give a windowed reduction. Here we take running sums of 3 values.

+↗️
    +˝˘3 2,6,0,1,4,3
 ⟨ 8 7 5 8 ⟩
 
-

A common task is to act on windows with an initial or final element so the total length stays the same. When using windows of length 2, the best way to accomplish this is with shift functions like « or ». If the window length is longer or variable, then a trick with Windows works better: add the elements, and then use windows matching the original length. Here we invert +`, which requires we take pairwise differences starting at initial value 0.

-↗️
    -(0»⊢) +` 3211
+

A common task is to act on windows with an initial or final element so the total length stays the same. When using windows of length 2, the best way to accomplish this is with a shift « or ». If the window length is longer or variable, then a trick with Windows works better: add the elements, and then use windows matching the original length. Here we invert Plus Scan +`, which requires we take pairwise differences starting at initial value 0.

+↗️
    -(0»⊢) +` 3211
 ⟨ 3 2 1 1 ⟩
+
     (-˜˝≠↕0∾⊢) +` 3211
 ⟨ 3 2 1 1 ⟩
 
-

With Windows, we can modify the 3-element running sum above to keep the length constant by starting with two zeros.

+

With Windows, we can modify the 3-element running sum from before to keep the length constant by starting with two zeros.

↗️
    (+˝≠↕(20)) 2,6,0,1,4,3
 ⟨ 2 8 8 7 5 8 ⟩
 
-- cgit v1.2.3