Replicate: Difference between revisions

Jump to navigation Jump to search
378 bytes added ,  22:18, 10 September 2022
m
Text replacement - "</source>" to "</syntaxhighlight>"
m (Text replacement - "</source>" to "</syntaxhighlight>")
Line 1: Line 1:
{{Built-ins|Replicate|/|⌿}}, or '''Copy''' (<source lang=j inline>#</source>) in [[J]], is a [[dyadic function]] or [[monadic operator]] that copies each [[element]] of the right [[argument]] a given number of times, ordering the copies along a specified [[axis]]. Typically <source lang=apl inline>/</source> is called Replicate while <source lang=apl inline>⌿</source> is called "Replicate First" or an equivalent. Replicate is a widely-accepted extension of the function '''Compress''', which requires the number of copies to be [[Boolean]]: each element is either retained (1 copy) or discarded (0 copies). Replicate with a Boolean left argument or [[operand]] may still be called "Compress".
{{Built-ins|Replicate|/|⌿}}, or '''Copy''' (<source lang=j inline>#</syntaxhighlight>) in [[J]], is a [[dyadic function]] or [[monadic operator]] that copies each [[element]] of the right [[argument]] a given number of times, ordering the copies along a specified [[axis]]. Typically <source lang=apl inline>/</syntaxhighlight> is called Replicate while <source lang=apl inline>⌿</syntaxhighlight> is called "Replicate First" or an equivalent. Replicate is a widely-accepted extension of the function '''Compress''', which requires the number of copies to be [[Boolean]]: each element is either retained (1 copy) or discarded (0 copies). Replicate with a Boolean left argument or [[operand]] may still be called "Compress".


Replicate is usually associated with [[Expand]] (<source lang=apl inline>\</source>), and the two functions are related to [[Mask]] and [[Mesh]]. It is also closely related to the [[Indices]] function. It shares a [[glyph]] with [[Reduce]] even though Replicate is naturally a [[function]] and Reduce must be an [[operator]]. This incongruity is sometimes resolved by making Replicate an operator itself, and sometimes by [[function-operator overloading]] allowing both syntactic elements to coexist.
Replicate is usually associated with [[Expand]] (<source lang=apl inline>\</syntaxhighlight>), and the two functions are related to [[Mask]] and [[Mesh]]. It is also closely related to the [[Indices]] function. It shares a [[glyph]] with [[Reduce]] even though Replicate is naturally a [[function]] and Reduce must be an [[operator]]. This incongruity is sometimes resolved by making Replicate an operator itself, and sometimes by [[function-operator overloading]] allowing both syntactic elements to coexist.


Outside of APL, [[wikipedia:filter (higher-order function)|filter]] typically provides the functionality of Compress, while Replicate has no common equivalent.
Outside of APL, [[wikipedia:filter (higher-order function)|filter]] typically provides the functionality of Compress, while Replicate has no common equivalent.
Line 11: Line 11:
       1 1 0 1 0 1 0 0 / 'compress'
       1 1 0 1 0 1 0 0 / 'compress'
cope
cope
</source>
</syntaxhighlight>
If the right argument is an array of [[indices]] generated by [[Iota]], Replicate resembles the function [[Indices]].
If the right argument is an array of [[indices]] generated by [[Iota]], Replicate resembles the function [[Indices]].
<source lang=apl>
<source lang=apl>
       1 1 0 0 1 / ⍳5
       1 1 0 0 1 / ⍳5
1 2 5
1 2 5
</source>
</syntaxhighlight>
With an array of non-negative integers, Replicate copies each element of the right argument the corresponding number of times. As with Compress, these copies retain their original ordering, and the length of the result is the sum of the control array.
With an array of non-negative integers, Replicate copies each element of the right argument the corresponding number of times. As with Compress, these copies retain their original ordering, and the length of the result is the sum of the control array.
<source lang=apl>
<source lang=apl>
Line 25: Line 25:
       ⍴ 0 3 0 0 2 0 1 0 2 / 'replicate'
       ⍴ 0 3 0 0 2 0 1 0 2 / 'replicate'
8
8
</source>
</syntaxhighlight>
Replicate usually allows [[scalar extension]] of the left argument, which results in every element being copied a fixed number of times.
Replicate usually allows [[scalar extension]] of the left argument, which results in every element being copied a fixed number of times.
<source lang=apl>
<source lang=apl>
       3 / 'replicate'
       3 / 'replicate'
rrreeepppllliiicccaaattteee
rrreeepppllliiicccaaattteee
</source>
</syntaxhighlight>


=== Negative numbers ===
=== Negative numbers ===
Line 37: Line 37:
       0 2 ¯3 1 / ⍳4
       0 2 ¯3 1 / ⍳4
2 2 0 0 0 4
2 2 0 0 0 4
</source>{{Works in|[[NARS2000]], [[Dyalog APL]], [[APLX]], [[ngn/apl]]}}
</syntaxhighlight>{{Works in|[[NARS2000]], [[Dyalog APL]], [[APLX]], [[ngn/apl]]}}
<source lang=apl>
<source lang=apl>
       0 2 ¯3 1 / ⍳3
       0 2 ¯3 1 / ⍳3
2 2 0 0 0 3
2 2 0 0 0 3
</source>{{Works in|[[APL2]], [[APLX]], [[GNU APL]]}}
</syntaxhighlight>{{Works in|[[APL2]], [[APLX]], [[GNU APL]]}}
The extensions are the same when the right argument is subject to [[singleton extension]]. This extension was usually supported before any extension to negative numbers, but would not typically be useful because <source lang=apl inline>v/s</source> {{←→}} <source lang=apl inline>(+/v)/s</source> where <source lang=apl inline>v</source> is a non-negative integer vector and <source lang=apl inline>s</source> is a singleton.
The extensions are the same when the right argument is subject to [[singleton extension]]. This extension was usually supported before any extension to negative numbers, but would not typically be useful because <source lang=apl inline>v/s</syntaxhighlight> {{←→}} <source lang=apl inline>(+/v)/s</syntaxhighlight> where <source lang=apl inline>v</syntaxhighlight> is a non-negative integer vector and <source lang=apl inline>s</syntaxhighlight> is a singleton.
<source lang=apl>
<source lang=apl>
       1 ¯2 3 / 'a'
       1 ¯2 3 / 'a'
a  aaa
a  aaa
</source>{{Works in|[[NARS2000]], [[APL2]], [[Dyalog APL]], [[APLX]], [[ngn/apl]], [[GNU APL]]}}
</syntaxhighlight>{{Works in|[[NARS2000]], [[APL2]], [[Dyalog APL]], [[APLX]], [[ngn/apl]], [[GNU APL]]}}


=== High-rank arrays ===
=== High-rank arrays ===
Replicate works along a particular [[axis]], which can be specified in languages with [[function axis]] and otherwise is the first axis for <source lang=apl inline>⌿</source>, and the last axis for <source lang=apl inline>/</source> (except in [[A+]], which uses <source lang=apl inline>/</source> for the [[Leading axis theory|first-axis]] form and has no last-axis form).
Replicate works along a particular [[axis]], which can be specified in languages with [[function axis]] and otherwise is the first axis for <source lang=apl inline>⌿</syntaxhighlight>, and the last axis for <source lang=apl inline>/</syntaxhighlight> (except in [[A+]], which uses <source lang=apl inline>/</syntaxhighlight> for the [[Leading axis theory|first-axis]] form and has no last-axis form).
<source lang=apl>
<source lang=apl>
       ⎕←A ← 4 6⍴⎕A
       ⎕←A ← 4 6⍴⎕A
Line 66: Line 66:
MNOPQR
MNOPQR
STUVWX
STUVWX
</source>
</syntaxhighlight>
[[APL2]] further extends the [[singleton extension]] of the right argument, allowing it to have length 1 along the replication axis even if other axes have lengths not equal to 1.
[[APL2]] further extends the [[singleton extension]] of the right argument, allowing it to have length 1 along the replication axis even if other axes have lengths not equal to 1.
<source lang=apl>
<source lang=apl>
Line 73: Line 73:
b  bbb
b  bbb
c  ccc
c  ccc
</source>{{Works in|[[APL2]], [[Dyalog APL]], [[APLX]], [[ngn/apl]], [[GNU APL]]}}
</syntaxhighlight>{{Works in|[[APL2]], [[Dyalog APL]], [[APLX]], [[ngn/apl]], [[GNU APL]]}}


[[dzaima/APL]] expects arguments of <source lang=apl inline>⌿</source> to have matching shape, and replicates the [[ravel]] of both.
[[dzaima/APL]] expects arguments of <source lang=apl inline>⌿</syntaxhighlight> to have matching shape, and replicates the [[ravel]] of both.


=== Operator or function? ===
=== Operator or function? ===
{{Main|Function-operator overloading}}
{{Main|Function-operator overloading}}
The syntax <source lang=apl inline>a / b</source> is ambiguous: it may be an invocation of a [[dyadic function]] <source lang=apl inline>/</source> with left [[argument]] <source lang=apl inline>a</source> and right argument <source lang=apl inline>b</source>, or of a [[monadic operator]] with [[operand]] <source lang=apl inline>a</source> and right argument <source lang=apl inline>b</source>. In early APLs there was no way to resolve this ambiguity, but with the extension of [[operator]]s to allow arbitrary function operands instead of a specified set of [[primitive function]]s, the distinction becomes apparent: a function Replicate can be used as an [[operand]] while an operator Replicate cannot.
The syntax <source lang=apl inline>a / b</syntaxhighlight> is ambiguous: it may be an invocation of a [[dyadic function]] <source lang=apl inline>/</syntaxhighlight> with left [[argument]] <source lang=apl inline>a</syntaxhighlight> and right argument <source lang=apl inline>b</syntaxhighlight>, or of a [[monadic operator]] with [[operand]] <source lang=apl inline>a</syntaxhighlight> and right argument <source lang=apl inline>b</syntaxhighlight>. In early APLs there was no way to resolve this ambiguity, but with the extension of [[operator]]s to allow arbitrary function operands instead of a specified set of [[primitive function]]s, the distinction becomes apparent: a function Replicate can be used as an [[operand]] while an operator Replicate cannot.


One test of Replicate's nature is to try Replicate [[Each]]<ref>Benkard, J. Philip. [https://dl.acm.org/doi/10.1145/384282.28345 "Replicate each, anyone?"]. [[APL87]].</ref> with an expression such as <source lang=apl inline>1 3 /¨ 'ab' 'cd'</source>. If Replicate is implemented as an operator, it will be applied to the operand <source lang=apl inline>1 3</source>, and Each will be applied to the resulting [[derived function]] <source lang=apl inline>1 3/</source>.
One test of Replicate's nature is to try Replicate [[Each]]<ref>Benkard, J. Philip. [https://dl.acm.org/doi/10.1145/384282.28345 "Replicate each, anyone?"]. [[APL87]].</ref> with an expression such as <source lang=apl inline>1 3 /¨ 'ab' 'cd'</syntaxhighlight>. If Replicate is implemented as an operator, it will be applied to the operand <source lang=apl inline>1 3</syntaxhighlight>, and Each will be applied to the resulting [[derived function]] <source lang=apl inline>1 3/</syntaxhighlight>.
<source lang=apl>
<source lang=apl>
       1 3 /¨ 'ab' 'cd'   
       1 3 /¨ 'ab' 'cd'   
Line 87: Line 87:
       (1 3/)¨ 'ab' 'cd'
       (1 3/)¨ 'ab' 'cd'
  abbb  cddd
  abbb  cddd
</source>{{Works in|[[SHARP APL]] (with <source lang=apl inline>¨></source> in place of <source lang=apl inline>¨</source>), [[APL2]], [[APLX]]}}
</syntaxhighlight>{{Works in|[[SHARP APL]] (with <source lang=apl inline>¨></syntaxhighlight> in place of <source lang=apl inline>¨</syntaxhighlight>), [[APL2]], [[APLX]]}}
If Replicate is a function, then Each will apply to Replicate only, and the resulting derived function will be invoked monadically.
If Replicate is a function, then Each will apply to Replicate only, and the resulting derived function will be invoked monadically.
<source lang=apl>
<source lang=apl>
Line 94: Line 94:
       1 3 (/¨) 'ab' 'cd'
       1 3 (/¨) 'ab' 'cd'
  ab  cccddd
  ab  cccddd
</source>{{Works in|[[NARS2000]], [[Dyalog APL]], [[GNU APL]]}}
</syntaxhighlight>{{Works in|[[NARS2000]], [[Dyalog APL]], [[GNU APL]]}}
In early APLs such as [[APL\360]], applying an operator to Compress will always result in a [[SYNTAX ERROR]], because Compress is not an allowed operand of any operator. This is also the case in [[ngn/apl]]: although operators can apply to any function, Replicate cannot be used unless both arguments are immediately available. In both cases there is no way to determine whether Replicate "acts like a function" or "acts like an operator".
In early APLs such as [[APL\360]], applying an operator to Compress will always result in a [[SYNTAX ERROR]], because Compress is not an allowed operand of any operator. This is also the case in [[ngn/apl]]: although operators can apply to any function, Replicate cannot be used unless both arguments are immediately available. In both cases there is no way to determine whether Replicate "acts like a function" or "acts like an operator".


== History ==
== History ==


Compress was described in [[A Programming Language]], where it was written with the symbols <math>/</math> and <math>/\!\!/</math>. In [[Iverson notation]] compression was particularly important because [[Take]] and [[Drop]] could be performed only by compression with a [[prefix]] or [[suffix]] vector. It was included in [[APL\360]], which changed the doubled slash to a barred slash <source lang=apl inline>⌿</source>, and allowed a [[specified axis]] and [[singleton extension]] on both sides (very briefly, singleton extension was allowed only for the right argument<ref>Falkoff, A.D., and K.E. Iverson, "[http://keiapl.org/archive/APL360_UsersMan_Aug1968.pdf APL\360 User's Manual]". IBM, August 1968.</ref>). The APL\360 definition continued to be included in APLs unchanged until 1980.
Compress was described in [[A Programming Language]], where it was written with the symbols <math>/</math> and <math>/\!\!/</math>. In [[Iverson notation]] compression was particularly important because [[Take]] and [[Drop]] could be performed only by compression with a [[prefix]] or [[suffix]] vector. It was included in [[APL\360]], which changed the doubled slash to a barred slash <source lang=apl inline>⌿</syntaxhighlight>, and allowed a [[specified axis]] and [[singleton extension]] on both sides (very briefly, singleton extension was allowed only for the right argument<ref>Falkoff, A.D., and K.E. Iverson, "[http://keiapl.org/archive/APL360_UsersMan_Aug1968.pdf APL\360 User's Manual]". IBM, August 1968.</ref>). The APL\360 definition continued to be included in APLs unchanged until 1980.


In 1980, [[Bob Bernecky]] introduced the extension Replicate to [[SHARP APL]]: he allowed an operand (since SHARP's Replicate is an operator) consisting of non-negative integers rather than just [[Boolean]]s to indicate the number of times to copy.<ref>[[Bob Bernecky|Bernecky, Bob]]. SATN-34: Replication. [[IPSA]]. 1980-08-15.</ref> This extension was rapidly and widely adopted, starting with [[NARS]] in 1981, and is now a feature of the [[ISO/IEC 13751:2001]] standard.
In 1980, [[Bob Bernecky]] introduced the extension Replicate to [[SHARP APL]]: he allowed an operand (since SHARP's Replicate is an operator) consisting of non-negative integers rather than just [[Boolean]]s to indicate the number of times to copy.<ref>[[Bob Bernecky|Bernecky, Bob]]. SATN-34: Replication. [[IPSA]]. 1980-08-15.</ref> This extension was rapidly and widely adopted, starting with [[NARS]] in 1981, and is now a feature of the [[ISO/IEC 13751:2001]] standard.
Line 105: Line 105:
Two extensions to allow negative numbers in the left argument have been introduced, in each case specifying that the negative of a number indicates that many [[fill element]]s should appear in the result. In 1981 [[NARS]] specified that these fill elements replace the corresponding right argument element, so that the lengths of the left and right arguments are always equal, and extended [[Expand]] similarly. [[APL2]], in 1984, made the opposite choice, so that the length of the right argument along the specified axis is equal to the number of non-negative elements on the left. [[APL2]] also loosened the [[conformability]] requirements further than simply allowing [[singleton extension]]: it allowed a right argument with length 1 along the replication axis to be extended. [[Dyalog APL]], created before APL2, adopted the [[NARS]] definition for negative elements but added APL2 conformability extension in [[Dyalog APL 13.1|version 13.1]]. Later [[APLX]] took advantage of the fact that the two negative number extensions can be distinguished by the length of the left argument, and implemented every NARS and APL2 extension.
Two extensions to allow negative numbers in the left argument have been introduced, in each case specifying that the negative of a number indicates that many [[fill element]]s should appear in the result. In 1981 [[NARS]] specified that these fill elements replace the corresponding right argument element, so that the lengths of the left and right arguments are always equal, and extended [[Expand]] similarly. [[APL2]], in 1984, made the opposite choice, so that the length of the right argument along the specified axis is equal to the number of non-negative elements on the left. [[APL2]] also loosened the [[conformability]] requirements further than simply allowing [[singleton extension]]: it allowed a right argument with length 1 along the replication axis to be extended. [[Dyalog APL]], created before APL2, adopted the [[NARS]] definition for negative elements but added APL2 conformability extension in [[Dyalog APL 13.1|version 13.1]]. Later [[APLX]] took advantage of the fact that the two negative number extensions can be distinguished by the length of the left argument, and implemented every NARS and APL2 extension.


[[A+]] and [[J]] modified Replicate to fit [[leading axis theory]]. Rather than allow Replicate to operate on any axis they have only one Replicate function (in A+, <source lang=apl inline>/</source>; in J, <source lang=j inline>#</source>) which works on the first axis—it copies [[major cell]]s rather than elements. Both languages rejected the [[NARS]] extension to negative left arguments, but J introduced its own system to add [[fill element]]s by allowing [[complex number]]s in the left argument, and removed the [[Expand]] function entirely. [[Arthur Whitney]] went on to make a more radical change in [[K]], removing Replicate entirely in favor of [[Where]].
[[A+]] and [[J]] modified Replicate to fit [[leading axis theory]]. Rather than allow Replicate to operate on any axis they have only one Replicate function (in A+, <source lang=apl inline>/</syntaxhighlight>; in J, <source lang=j inline>#</syntaxhighlight>) which works on the first axis—it copies [[major cell]]s rather than elements. Both languages rejected the [[NARS]] extension to negative left arguments, but J introduced its own system to add [[fill element]]s by allowing [[complex number]]s in the left argument, and removed the [[Expand]] function entirely. [[Arthur Whitney]] went on to make a more radical change in [[K]], removing Replicate entirely in favor of [[Where]].


== Extension support ==
== Extension support ==
Line 131: Line 131:
| [[APL2]]                                            || Operator  || {{Yes}} || {{No}}  || {{Yes}} || {{Yes|APL2}}        || {{Yes}} ||
| [[APL2]]                                            || Operator  || {{Yes}} || {{No}}  || {{Yes}} || {{Yes|APL2}}        || {{Yes}} ||
|-
|-
| [[A+]] (<source lang=apl inline>/</source>)        || Function  || {{Yes}} ||colspan=2 {{No}}    || {{Maybe|Single}}    || {{No}}  ||
| [[A+]] (<source lang=apl inline>/</syntaxhighlight>)        || Function  || {{Yes}} ||colspan=2 {{No}}    || {{Maybe|Single}}    || {{No}}  ||
|-
|-
| [[J]] (<source lang=j inline>#</source>)            || Function  || {{Yes}} ||colspan=2 {{No}}    || {{Maybe|Scalar}}      || {{No}}  || Complex left argument allowed
| [[J]] (<source lang=j inline>#</syntaxhighlight>)            || Function  || {{Yes}} ||colspan=2 {{No}}    || {{Maybe|Scalar}}      || {{No}}  || Complex left argument allowed
|-
|-
| [[ISO/IEC 13751:2001]]                              || Function  || {{Yes}} ||colspan=2 {{No}}    || {{Maybe|Scalar}}      || {{Yes}} ||
| [[ISO/IEC 13751:2001]]                              || Function  || {{Yes}} ||colspan=2 {{No}}    || {{Maybe|Scalar}}      || {{Yes}} ||
Line 143: Line 143:
| [[GNU APL]]                                        || Function  || {{Yes}} || {{No}}  || {{Yes}} || {{Yes|APL2}}        || {{Yes}} ||
| [[GNU APL]]                                        || Function  || {{Yes}} || {{No}}  || {{Yes}} || {{Yes|APL2}}        || {{Yes}} ||
|-
|-
| [[dzaima/APL]] (<source lang=apl inline>⌿</source>) || Function  || {{Yes}} || {{Yes}} || {{No}}  || {{No}}            || {{No}}  ||
| [[dzaima/APL]] (<source lang=apl inline>⌿</syntaxhighlight>) || Function  || {{Yes}} || {{Yes}} || {{No}}  || {{No}}            || {{No}}  ||
|-
|-
| [[BQN]] (<source lang=apl inline>/</source>)        || Function  || {{Yes}} ||colspan=2 {{No}}    || {{No}}            || {{No}}  || Multiple leading axes supported
| [[BQN]] (<source lang=apl inline>/</syntaxhighlight>)        || Function  || {{Yes}} ||colspan=2 {{No}}    || {{No}}            || {{No}}  || Multiple leading axes supported
|}
|}


Line 152: Line 152:
== Outside of APL ==
== Outside of APL ==


While Replicate is rarely used in non-array programming languages, Compress is sometimes seen. Usually the same functionality is provided by the higher-order function [[wikipedia:filter (higher-order function)|filter]], which an APLer might define as the [[monadic operator]] <source lang=apl inline>filter←{(⍺⍺¨ ⍵) / ⍵}</source> on a [[vector]] argument.
While Replicate is rarely used in non-array programming languages, Compress is sometimes seen. Usually the same functionality is provided by the higher-order function [[wikipedia:filter (higher-order function)|filter]], which an APLer might define as the [[monadic operator]] <source lang=apl inline>filter←{(⍺⍺¨ ⍵) / ⍵}</syntaxhighlight> on a [[vector]] argument.


While filter is similar to Compress, some extensions to the [[wikipedia:x86|x86]] instruction set are exactly equivalent to Compress on particular data types. In [[wikipedia:BMI2|BMI2]], the PEXT and PDEP instructions (parallel bit extract and deposit) are identical to Compress and [[Expand]] on the bits of a register argument. Indeed, [[Dyalog APL]] uses these instructions to implement those primitives (see [[Dyalog APL#Instruction set usage]]). The [[wikipedia:AVX-512|AVX-512]] instructions VPCOMPRESSQ and VPEXPANDQ (and variations) are not only equivalent to Compress and Expand using a mask register for the [[Boolean]] argument and a vector register for the other argument, but are named after the APL functions. These instructions allow compression of 4-byte and 8-byte elements, and with AVX-512_VBMI2 support was added for 1-byte and 2-byte elements as well.
While filter is similar to Compress, some extensions to the [[wikipedia:x86|x86]] instruction set are exactly equivalent to Compress on particular data types. In [[wikipedia:BMI2|BMI2]], the PEXT and PDEP instructions (parallel bit extract and deposit) are identical to Compress and [[Expand]] on the bits of a register argument. Indeed, [[Dyalog APL]] uses these instructions to implement those primitives (see [[Dyalog APL#Instruction set usage]]). The [[wikipedia:AVX-512|AVX-512]] instructions VPCOMPRESSQ and VPEXPANDQ (and variations) are not only equivalent to Compress and Expand using a mask register for the [[Boolean]] argument and a vector register for the other argument, but are named after the APL functions. These instructions allow compression of 4-byte and 8-byte elements, and with AVX-512_VBMI2 support was added for 1-byte and 2-byte elements as well.

Navigation menu