Rotate
⌽ ⊖
|
Rotate (⌽, ⊖) is a dyadic primitive function which "rotates" the elements of the right argument around a specified axis. The name Rotate is typically used for the primitive ⌽, which rotates along the last axis, while ⊖, which rotates along the first axis, is called "Rotate First" or similar. APLs with function axis allow to choose a different axis from the default one. In the leading axis model, specifying an axis is discouraged in favor of using ⊖ with the Rank operator.
Rotate and Rotate First share the glyphs ⌽ and ⊖ with monadic functions Reverse and Reverse First, respectively.
Examples
The left argument is usually restricted to a scalar. Rotate on a vector right argument rotates the elements to the left, wrapping around as necessary. The left argument can be large or negative.
3⌽1 2 3 4 5 6 7
4 5 6 7 1 2 3
8⌽1 2 3 4 5 6 7
2 3 4 5 6 7 1
¯4⌽1 2 3 4 5 6 7
4 5 6 7 1 2 3
Rotate on a matrix rotates the elements around horizontally (to the left), while Rotate First does vertically (upwards).
⎕←M←3 4⍴⎕A
ABCD
EFGH
IJKL
2⌽M ⍝ Horizontally rotate twice
CDAB
GHEF
KLIJ
1⊖M ⍝ Vertically rotate once
EFGH
IJKL
ABCD
Higher-rank arrays can be rotated on an arbitrary axis if function axis or Rank operator is supported:
⎕←M←2 3 4⍴⎕A
ABCD
EFGH
IJKL
MNOP
QRST
UVWX
(1⌽[1]M)(1⌽[2]M)(1⌽[3]M) ⍝ Rotate once over 1st, 2nd, or 3rd axis, using function axis
┌────┬────┬────┐
│MNOP│EFGH│BCDA│
│QRST│IJKL│FGHE│
│UVWX│ABCD│JKLI│
│ │ │ │
│ABCD│QRST│NOPM│
│EFGH│UVWX│RSTQ│
│IJKL│MNOP│VWXU│
└────┴────┴────┘
(1⊖⍤3⊢M)(1⊖⍤2⊢M)(1⊖⍤1⊢M) ⍝ Same as above, using Rotate First with Rank
┌────┬────┬────┐
│MNOP│EFGH│BCDA│
│QRST│IJKL│FGHE│
│UVWX│ABCD│JKLI│
│ │ │ │
│ABCD│QRST│NOPM│
│EFGH│UVWX│RSTQ│
│IJKL│MNOP│VWXU│
└────┴────┴────┘
When the right argument's rank is higher than 1, the left argument can be an array with rank 1 smaller. It specifies different amounts of rotation for different "columns" or 1-dimensional subarrays which Rotate is applied to.
⎕←M←3 4⍴⍳4
1 2 3 4
1 2 3 4
1 2 3 4
1 3 2⌽M ⍝ Rotate 1st row once, 2nd row thrice, and 3rd row twice
2 3 4 1
4 1 2 3
3 4 1 2
Multi-axis rotation
In J and later BQN, Uiua, and TinyAPL, the traditional APL form for an array left argument is not used. Instead, the left argument can be a vector with length less than or equal to the right argument's rank, specifying a different rotation amount along any number of leading axes.
i.3 4
0 1 2 3
4 5 6 7
8 9 10 11
1 2|.i.3 4 NB. Rotate vertically once, horizontally twice
6 7 4 5
10 11 8 9
2 3 0 1Description
In languages with function axis, exactly one argument axis may be specified.
Rotating a scalar always yields that scalar unchanged. Otherwise, Rotate operates on a particular axis of its right argument. This axis is the specified axis if one is given, and otherwise the last axis for ⌽, or the first axis for ⊖.
The result array has the same shape and elements as the right argument array, but the elements cyclically move around the rotation axis. Consequently if the length of this axis is 0 or 1 then rotation has no effect.
APL model
The rotation of a vector Y by X units may be written in any APL, assuming ⎕IO←0, as Y[(⍴Y)|X+⍳⍴Y]. To rotate an arbitrary array Squad indexing with axis (or rank) is helpful.
RotateAxis ← { ⍝ R ← X (K RotateAxis) Y
⎕IO←0
0=≢⍴⍵: ⍵ ⍝ Return a scalar unchanged
l ← ⍺⍺ ⌷ ⍴⍵ ⍝ Length of rotation axis
(⊂l|⍺+⍳l) ⌷[⍺⍺] ⍵ ⍝ Rotate with indexing
}
History
In Iverson notation, rotation was written with an arrow, for left rotation and for right rotation (sometimes with a default left argument of 1). These had not yet been related to Reverse, which beginning with Formalism in Programming Languages[1] in 1963 was drawn with a circle and line much like APL's ⌽. The arrow notation was implemented in IVSYS/7090 (dyadic only), but by the first APL\360 publication[2] it had changed to the single primitive ⌽ for left rotation, with negative numbers used for right rotation. The up and down arrows were briefly unused, until they were taken for Take and Drop. Although Rotate could take an axis specification, the glyph ⊖ for Rotate First was not in this first publication; it was added by APL\360's release to the public in 1968.
Direction
Although it's not obvious why Rotate by a positive argument should move elements left and not right, a few reasons can be given. The choice may be seen as reflecting an abstract principle that it's cleaner to manipulate argument indices than result indices, which has various practical consequences.
One reason is that a left rotation causes Rotate to split its argument at the same point as Take and Drop, before the element with index ⍺+⎕IO. Given a vector v and n≤⍴v, we can represent n⌽v as (n↓v),(n↑v), or n(↓,↑)v. However, a right rotation (-n)⌽v has no similar representation in terms of positive n. For example, 2⌽v←'abcdefg' is 'cdefgab', which can be formed from 'cdefg'≡2↓v and 'ab'≡2↑v, but ¯2⌽v is 'fgabcde', which can't because neither string starts with 'f' or ends with 'e'. As APL\360 did not use Take and Drop but rather the symmetric prefix and suffix vectors when Rotate's direction was chosen, this is probably not the historical justification.
Another consideration is the permutation vector for rotate, which connects it to Indexing. This is seen as l|⍺+⍳l in the APL model above. Given a vector v and length l←≢v, and assuming ⎕IO←0, we have n⌽v ←→ v[l|n+⍳l]. Rotation in the opposite direction satisfies (-n)⌽v ←→ v[l|(⍳l)-n] instead, requiring subtraction instead of addition. An additive formula (-n)⌽v[l|n+⍳l] ←→ v is possible, but it corresponds to an inverse permutation, and can't be used directly as the definition of (-n)⌽v. Another indexing-related property is that the first element of n⌽v is v[n+⎕IO], and similarly, by repeatedly setting v←1⌽v, the first element of v cycles through its original values in order.
External links
Lessons
Documentation
References
- ↑ Ken Iverson. Formalism in Programming Languages. Communications of the ACM, Volume 7, Number 2. 1964-02.
- ↑ Falkoff, A.D., and K.E. Iverson. "The APL\360 Terminal System". Research Report RC-1922, IBM, 1967-10-16.