⊂⊃⊆⌷⍋⍒
Contents
⊂⊃⊆⌷⍋⍒
#
Enclose ⊂
#
Monadic ⊂
encloses its argument. It packages an arbitrary structure into a scalar. Simple scalars cannot be enclosed. We can turn on boxed output with the ]box
user command to illustrate APL’s array structure in more detail:
]box on -s=max
┌→────────────────┐ │Was ON -style=max│ └─────────────────┘
v←1 2 3 4
v
⊂v
┌→──────┐ │1 2 3 4│ └~──────┘
┌───────────┐ │ ┌→──────┐ │ │ │1 2 3 4│ │ │ └~──────┘ │ └∊──────────┘
The little epsilon ∊
in the bottom of the outer box indicates the enclosure.
If we tally an enclosed structure, it should come out as 1:
≢⊂v ⍝ an enclosed vector is a scalar
1
Here’s another example:
(3 3⍴⎕A),(3 3⍴⎕A) ⍝ concatenation of two matrices.
(⊂3 3⍴⎕A),(⊂3 3⍴⎕A) ⍝ concatenation of two enclosed matrices
┌→─────┐ ↓ABCABC│ │DEFDEF│ │GHIGHI│ └──────┘
┌→────────────┐ │ ┌→──┐ ┌→──┐ │ │ ↓ABC│ ↓ABC│ │ │ │DEF│ │DEF│ │ │ │GHI│ │GHI│ │ │ └───┘ └───┘ │ └∊────────────┘
The first gave us a matrix of shape 3 6, the second gave a vector of shape 2.
(3 3⍴⎕A),(⊂3 3⍴⎕A) ⍝ concatenation of a matrix and an enclosed matrix
┌→────────────┐ ↓ ┌→──┐ │ │ A B C ↓ABC│ │ │ - - - │DEF│ │ │ │GHI│ │ │ └───┘ │ │ ┌→──┐ │ │ D E F ↓ABC│ │ │ - - - │DEF│ │ │ │GHI│ │ │ └───┘ │ │ ┌→──┐ │ │ G H I ↓ABC│ │ │ - - - │DEF│ │ │ │GHI│ │ │ └───┘ │ └∊────────────┘
Concatenating a scalar to a matrix uses the scalar for each row. Here the entire right-hand matrix was treated as a scalar because it was enclosed.
(3 3⍴⎕A),'x'
┌→───┐ ↓ABCx│ │DEFx│ │GHIx│ └────┘
So you can (and should) use ⊂
to tell APL how you want the scalar extension (auto-vectorisation) to be applied.
⊂
is also good for treating text vectors as strings (i.e. in one piece):
'aaa' 'bbb' 'ccc' ⍳ 'aaa'
┌→────┐ │4 4 4│ └~────┘
This says that each one of the three right-side ‘a’s is found in position 4 (i.e. are not) in the left-side list.
'aaa' 'bbb' 'ccc' ⍳ ⊂'aaa'
1
This says that ‘aaa’ is the first string.
Partitioned enclose ⊂
#
Dyadic ⊂
is partitioned enclose. It encloses (hence sharing the symbol) pieces of the right argument as indicated by the left argument. Best to use an example:
1 0 0 1 0 1 0 0 0 1 0⊂'Hello World'
┌→───────────────────────┐ │ ┌→──┐ ┌→─┐ ┌→───┐ ┌→─┐ │ │ │Hel│ │lo│ │ Wor│ │ld│ │ │ └───┘ └──┘ └────┘ └──┘ │ └∊───────────────────────┘
This works on higher rank arrays, too. It partitions along the last axis:
1 0 1 0 0 1 0 1 0 0 0 1 1 ⊂ 2 13⍴⎕A
┌→───────────────────────────────┐ │ ┌→─┐ ┌→──┐ ┌→─┐ ┌→───┐ ┌→┐ ┌→┐ │ │ ↓AB│ ↓CDE│ ↓FG│ ↓HIJK│ ↓L│ ↓M│ │ │ │NO│ │PQR│ │ST│ │UVWX│ │Y│ │Z│ │ │ └──┘ └───┘ └──┘ └────┘ └─┘ └─┘ │ └∊───────────────────────────────┘
For vectors, 1⊂
is the same as ,¨
which may be useful in trains where you want to have a left argument.
For higher rank arrays, 1⊂
cuts into columns:
1 ⊂ 2 13⍴⎕A
┌→────────────────────────────────────────────────────┐ │ ┌→┐ ┌→┐ ┌→┐ ┌→┐ ┌→┐ ┌→┐ ┌→┐ ┌→┐ ┌→┐ ┌→┐ ┌→┐ ┌→┐ ┌→┐ │ │ ↓A│ ↓B│ ↓C│ ↓D│ ↓E│ ↓F│ ↓G│ ↓H│ ↓I│ ↓J│ ↓K│ ↓L│ ↓M│ │ │ │N│ │O│ │P│ │Q│ │R│ │S│ │T│ │U│ │V│ │W│ │X│ │Y│ │Z│ │ │ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ │ └∊────────────────────────────────────────────────────┘
You can use brackets to indicate which axis you wish to cut along:
1 ⊂[1] 2 13⍴⎕A
1 0 1 1 ⊂[1] 4 3⍴⎕A
┌→────────────────────────────────┐ │ ┌→────────────┐ ┌→────────────┐ │ │ ↓ABCDEFGHIJKLM│ ↓NOPQRSTUVWXYZ│ │ │ └─────────────┘ └─────────────┘ │ └∊────────────────────────────────┘
┌→──────────────────┐ │ ┌→──┐ ┌→──┐ ┌→──┐ │ │ ↓ABC│ ↓GHI│ ↓JKL│ │ │ │DEF│ └───┘ └───┘ │ │ └───┘ │ └∊──────────────────┘
Note that the left argument need not be the same length as the right argument. If it’s shorter, it’s assumed to consist of zeros to the end:
⍸⍣¯1⊢1 5 10
(⍸⍣¯1⊢1 5 10)⊂⎕A ⍝ note: left arg is length 10. right arg is length 26
┌→──────────────────┐ │1 0 0 0 1 0 0 0 0 1│ └~──────────────────┘
┌→───────────────────────────────────┐ │ ┌→───┐ ┌→────┐ ┌→────────────────┐ │ │ │ABCD│ │EFGHI│ │JKLMNOPQRSTUVWXYZ│ │ │ └────┘ └─────┘ └─────────────────┘ │ └∊───────────────────────────────────┘
Another common use of dyadic ⊂
is to split a vector into its head and tail:
1 1⊂1 2 3 4 5 6 7
┌→──────────────────┐ │ ┌→┐ ┌→──────────┐ │ │ │1│ │2 3 4 5 6 7│ │ │ └~┘ └~──────────┘ │ └∊──────────────────┘
The left argument does not have to be a Boolean vector, but can in fact be any simple numeric vector. We can think of each number as specifying the number of steps to the left of its next neighbour it is. In other words, we can generate empty segments:
1 0 1 0 1 0 1 0 ⊂ 'ABCDEFGH'
1 0 2 0 5 0 2 0 ⊂ 'ABCDEFGH'
1 0 0 2 2 0 0 0 0 0 0 ⊂ 'KenEIverson'
┌→────────────────────┐ │ ┌→─┐ ┌→─┐ ┌→─┐ ┌→─┐ │ │ │AB│ │CD│ │EF│ │GH│ │ │ └──┘ └──┘ └──┘ └──┘ │ └∊────────────────────┘
┌→────────────────────────────────────────────┐ │ ┌→─┐ ┌⊖┐ ┌→─┐ ┌⊖┐ ┌⊖┐ ┌⊖┐ ┌⊖┐ ┌→─┐ ┌⊖┐ ┌→─┐ │ │ │AB│ │ │ │CD│ │ │ │ │ │ │ │ │ │EF│ │ │ │GH│ │ │ └──┘ └─┘ └──┘ └─┘ └─┘ └─┘ └─┘ └──┘ └─┘ └──┘ │ └∊────────────────────────────────────────────┘
┌→────────────────────────────┐ │ ┌→──┐ ┌⊖┐ ┌→┐ ┌⊖┐ ┌→──────┐ │ │ │Ken│ │ │ │E│ │ │ │Iverson│ │ │ └───┘ └─┘ └─┘ └─┘ └───────┘ │ └∊────────────────────────────┘
Here’s a practical example. Let’s say we have some sorted data, and we’ll like to group it by interval.
values ← 3 14 15 35 65 89 92
cutoffs ← 0 20 40 60 80 100
We want to end up with (3 14 15) (,35) ⍬ (,65) (89 92)
. That is, all the numbers in the interval [0,20)
and in [20,40)
etc. To get the index of each value’s interval, we begin by applying Interval Index:
cutoffs ⍸ values
┌→────────────┐ │1 1 1 2 4 5 5│ └~────────────┘
Now, you might think that Key ⌸
could do the trick, but then we’d have to insert all the possible intervals.
(cutoffs⍸values) {⊂1↓⍵}⌸⍥{⍵,⍨⍳≢cutoffs} values
┌→────────────────────────────────────┐ │ ┌→──────┐ ┌→─┐ ┌⊖┐ ┌→─┐ ┌→────┐ ┌⊖┐ │ │ │3 14 15│ │35│ │0│ │65│ │89 92│ │0│ │ │ └~──────┘ └~─┘ └~┘ └~─┘ └~────┘ └~┘ │ └∊────────────────────────────────────┘
However, using partitioned enclose with a non-Boolean left argument, we can craft a much more elegant solution:
(1,2-⍨/cutoffs⍸values)⊂values
┌→────────────────────────────────┐ │ ┌→──────┐ ┌→─┐ ┌⊖┐ ┌→─┐ ┌→────┐ │ │ │3 14 15│ │35│ │0│ │65│ │89 92│ │ │ └~──────┘ └~─┘ └~┘ └~─┘ └~────┘ │ └∊────────────────────────────────┘
or, as a train,
cutoffs (⊢⊂⍨1,2-⍨/⍸) values
┌→────────────────────────────────┐ │ ┌→──────┐ ┌→─┐ ┌⊖┐ ┌→─┐ ┌→────┐ │ │ │3 14 15│ │35│ │0│ │65│ │89 92│ │ │ └~──────┘ └~─┘ └~┘ └~─┘ └~────┘ │ └∊────────────────────────────────┘
Here’s another handy trick. Let’s say we want to split a vector at a given set of indices, in other words,
mask ← ⎕ ←'KenEIverson'∊⎕A
mask ⊂ 'KenEIverson'
┌→────────────────────┐ │1 0 0 1 1 0 0 0 0 0 0│ └~────────────────────┘
┌→────────────────────┐ │ ┌→──┐ ┌→┐ ┌→──────┐ │ │ │Ken│ │E│ │Iverson│ │ │ └───┘ └─┘ └───────┘ │ └∊────────────────────┘
…but if instead of the mask, we started with the indices of the 1s: 1 4 5?
(⍸⍣¯1⊢1 4 5)⊂'KenEIverson' ⍝ ⍸ has an inverse!
┌→────────────────────┐ │ ┌→──┐ ┌→┐ ┌→──────┐ │ │ │Ken│ │E│ │Iverson│ │ │ └───┘ └─┘ └───────┘ │ └∊────────────────────┘
Anyway, this shows another extension introduced in 18.0, namely that ⍸⍣¯1
conveniently works, but what you might not notice is that it also shows a further extension of ⊂
. Observe:
(⎕←⍸⍣¯1⊢1 4 5)⊂'KenEIverson'
┌→────────┐ │1 0 0 1 1│ └~────────┘ ┌→────────────────────┐ │ ┌→──┐ ┌→┐ ┌→──────┐ │ │ │Ken│ │E│ │Iverson│ │ │ └───┘ └─┘ └───────┘ │ └∊────────────────────┘
Note that the length of ⍸⍣¯1⊢1 4 5
doesn’t match the length of the string. Until 17.1, the arguments of ⊂
had to have exactly the same length. Now, the left argument can have any length up until 1+the length of the right argument, to allow some empty partitions at the end.
1 0 0 1 1 2⊂'Hello'
┌→──────────────────────┐ │ ┌→──┐ ┌→┐ ┌→┐ ┌⊖┐ ┌⊖┐ │ │ │Hel│ │l│ │o│ │ │ │ │ │ │ └───┘ └─┘ └─┘ └─┘ └─┘ │ └∊──────────────────────┘
So ⊂
will assume that any “missing” elements in its left argument are 0.
Disclose ⊃
#
Monadic ⊃
is disclose, which is pretty much the inverse of monadic ⊂
. It discloses a scalar (again, if possible; a simple scalar remains the same). If you use it on a high rank array (i.e. not enclosed), it will give you the first (top left) element:
3 3⍴⎕A ⍝ 3x3 matrix
⊂3 3⍴⎕A ⍝ enclosed
⊃⊂3 3⍴⎕A ⍝ disclose enclosed
⊃3 3⍴⎕A ⍝ dislclose unenclosed gives the first element
┌→──┐ ↓ABC│ │DEF│ │GHI│ └───┘
┌───────┐ │ ┌→──┐ │ │ ↓ABC│ │ │ │DEF│ │ │ │GHI│ │ │ └───┘ │ └∊──────┘
┌→──┐ ↓ABC│ │DEF│ │GHI│ └───┘
A -
The last feature (“first”) means that you can combine it with reverses etc, to get corner elements:
⊃⌽3 3⍴⎕A ⍝ top right
⊃⊖3 3⍴⎕A ⍝ bottom left
C -
G -
You can use it with ¨
(each) to get initials:
⊃¨'Kenneth' 'Eugene' 'Iverson'
┌→──┐ │KEI│ └───┘
Pick ⊃
#
Dyadic ⊃
is pick. It digs into nested arrays. Every scalar on its left is the index of an element in subsequent layers of nestedness:
(⊂2 3)⊃3 3⍴⎕A
2 3 1⊃(1 2 3)(4 5 (6 7 8))
F -
6
We can demonstrate this further. Here is a 2-by-2 matrix of two-element vectors:
2 2⍴(1 2)(3 4)(5 6)(7 8)
(1 2) 2⊃2 2⍴(1 2)(3 4)(5 6)(7 8)
┌→────────────┐ ↓ ┌→──┐ ┌→──┐ │ │ │1 2│ │3 4│ │ │ └~──┘ └~──┘ │ │ ┌→──┐ ┌→──┐ │ │ │5 6│ │7 8│ │ │ └~──┘ └~──┘ │ └∊────────────┘
4
In the last statement, the first index is 1 2, which picks the element (3 4), and the second index is 2, which picks the 4.
Nest ⊆
#
Monadic ⊆
is called nest because it guarantees you that the result is nested (non-simple). (1 2)(3 4 5)
is already nested, and ⊆
won’t do anything:
(1 2)(3 4 5)
⊆(1 2)(3 4 5)
┌→──────────────┐ │ ┌→──┐ ┌→────┐ │ │ │1 2│ │3 4 5│ │ │ └~──┘ └~────┘ │ └∊──────────────┘
┌→──────────────┐ │ ┌→──┐ ┌→────┐ │ │ │1 2│ │3 4 5│ │ │ └~──┘ └~────┘ │ └∊──────────────┘
1 2 3
is not nested, so ⊆
will nest it:
1 2 3
⊆1 2 3
┌→────┐ │1 2 3│ └~────┘
┌─────────┐ │ ┌→────┐ │ │ │1 2 3│ │ │ └~────┘ │ └∊────────┘
Works on higher rank too, of course:
2 3⍴⊂'abc'
⊆2 3⍴⊂'abc' ⍝ already nested, so no-op
┌→──────────────────┐ ↓ ┌→──┐ ┌→──┐ ┌→──┐ │ │ │abc│ │abc│ │abc│ │ │ └───┘ └───┘ └───┘ │ │ ┌→──┐ ┌→──┐ ┌→──┐ │ │ │abc│ │abc│ │abc│ │ │ └───┘ └───┘ └───┘ │ └∊──────────────────┘
┌→──────────────────┐ ↓ ┌→──┐ ┌→──┐ ┌→──┐ │ │ │abc│ │abc│ │abc│ │ │ └───┘ └───┘ └───┘ │ │ ┌→──┐ ┌→──┐ ┌→──┐ │ │ │abc│ │abc│ │abc│ │ │ └───┘ └───┘ └───┘ │ └∊──────────────────┘
2 3⍴'abc' ⍝ not nested
⊆2 3⍴'abc' ⍝ nested
┌→──┐ ↓abc│ │abc│ └───┘
┌───────┐ │ ┌→──┐ │ │ ↓abc│ │ │ │abc│ │ │ └───┘ │ └∊──────┘
Partition ⊆
#
Dyadic ⊆
is called partition (⊂
and ⊆
originate with different APL dialects, but Dyalog APL features both). To distinguish between them, we call ⊂
partitioned enclose and ⊆
just partition, but it doesn’t say much.
Dyadic ⊆
works similarly to dyadic ⊂
, but with different rules for the left argument. The left argument is non-negative integer instead of Boolean, and new partitions begin whenever an element is higher than its neighbour on the left. Also, elements indicated by 0s are dropped completely:
1 0 0 1 1 3 2 2 5 5 0⊆'Hello World'
┌→────────────────────┐ │ ┌→┐ ┌→─┐ ┌→──┐ ┌→─┐ │ │ │H│ │lo│ │ Wo│ │rl│ │ │ └─┘ └──┘ └───┘ └──┘ │ └∊────────────────────┘
1⊆array
is the same as ,⊂array
but uses a single dyadic function instead of two monadic ones, i.e. great for trains.
Materialise ⌷
#
Monadic ⌷
is materialise. It is almost the same as monadic ⊢
(i.e. identity). However, it will materialise the default property of a class. For collections, this means the Item property, so in effect it turns collections into vectors of items.
]dinput
:Class cl
:Property Default thing
:Access Public Shared
∇ r←get
r←3 1 4 1 4
∇
:EndProperty
:EndClass
cl.thing
⊢cl
⌷cl
┌→────────┐ │3 1 4 1 4│ └~────────┘
#.cl
┌→────────┐ │3 1 4 1 4│ └~────────┘
Index ⌷
#
Dyadic ⌷
is index. It is similar to pick, dyadic ⊃
, but works its way into the rank instead of the depth. On a 3D array, the first element selects layer, the second row, the third column:
2 3 4⍴⎕A
2⌷2 3 4⍴⎕A
2 1⌷2 3 4⍴⎕A
2 1 3⌷2 3 4⍴⎕A
┌┌→───┐ ↓↓ABCD│ ││EFGH│ ││IJKL│ ││ │ ││MNOP│ ││QRST│ ││UVWX│ └└────┘
┌→───┐ ↓MNOP│ │QRST│ │UVWX│ └────┘
┌→───┐ │MNOP│ └────┘
O -
Each element of the left argument may be may be any simple array:
(⊂1 1)⌷2 3 4⍴⎕A
2 (1 3)⌷2 3 4⍴⎕A ⍝ first and third row of second layer
(1 2)1 3⌷2 3 4⍴⎕A ⍝ third char of first row of layers 1 and 2
(1 2)(2 3)⌷2 3 4⍴⎕A ⍝ rows 2 and 3 of each of layers 1 and 2
┌┌→───┐ ↓↓ABCD│ ││EFGH│ ││IJKL│ ││ │ ││ABCD│ ││EFGH│ ││IJKL│ └└────┘
┌→───┐ ↓MNOP│ │UVWX│ └────┘
┌→─┐ │CO│ └──┘
┌┌→───┐ ↓↓EFGH│ ││IJKL│ ││ │ ││QRST│ ││UVWX│ └└────┘
Grade up/down ⍋⍒
#
Next up is ⍋
, called grade up. Monadic ⍋
takes a simple (non-nested) array and returns the indices of the major cells reordered so that they would order the array.
Easiest to understand with an example:
⍋3 1 4 1 5
┌→────────┐ │2 4 1 3 5│ └~────────┘
This means that the second element (1) is the smallest, then the fourth (1), then the first (3), etc. So, we can use this to sort the array:
3 1 4 1 5[⍋3 1 4 1 5]
┌→────────┐ │1 1 3 4 5│ └~────────┘
It works on high-rank arrays too:
3 2⍴2 7 1 8 2 8
⍋3 2⍴2 7 1 8 2 8
┌→──┐ ↓2 7│ │1 8│ │2 8│ └~──┘
┌→────┐ │2 1 3│ └~────┘
So the first is row 2 (1 8)
then row 1 (2 7)
then row 3 (2 8)
. It works on characters too, where it grades in Unicode point order:
5 2⍴'HelloWorld'
⍋5 2⍴'HelloWorld'
(5 2⍴'HelloWorld')[⍋5 2⍴'HelloWorld';]
┌→─┐ ↓He│ │ll│ │oW│ │or│ │ld│ └──┘
┌→────────┐ │1 5 2 3 4│ └~────────┘
┌→─┐ ↓He│ │ld│ │ll│ │oW│ │or│ └──┘
4 2 2⍴'Hello World PPCG'
⍋4 2 2⍴'Hello World PPCG' ⍝ layer grade up
┌┌→─┐ ↓↓He│ ││ll│ ││ │ ││o │ ││Wo│ ││ │ ││rl│ ││d │ ││ │ ││PP│ ││CG│ └└──┘
┌→──────┐ │1 4 2 3│ └~──────┘
Layer 1, layer 4, layer 2, layer 3:
{⍵[⍋⍵;;]}4 2 2⍴'Hello World PPCG'
┌┌→─┐ ↓↓He│ ││ll│ ││ │ ││PP│ ││CG│ ││ │ ││o │ ││Wo│ ││ │ ││rl│ ││d │ └└──┘
⍋⍋
is the cardinal numbers:
⍋'PPCG'
⍋⍋'PPCG'
┌→──────┐ │3 4 1 2│ └~──────┘
┌→──────┐ │3 4 1 2│ └~──────┘
So P is the third, P is the fourth, C is the first, and G is the second. Applying ⍋
to a permutation inverts it (swaps between cardinal and grade). Another way to think about it is that ⍋
is the indices of cells in the order that would sort them. ⍋⍋
is the position each will take when sorted. If you think about it hard, you’ll see why ⍋ swaps back and forth between these two.
Here’s an example where the grade and the cardinals differ:
⍋'random'
⍋⍋'random'
⍋⍋⍋'random' ⍝ grading the cardinals takes us back to grade
┌→──────────┐ │2 4 6 3 5 1│ └~──────────┘
┌→──────────┐ │6 1 4 2 5 3│ └~──────────┘
┌→──────────┐ │2 4 6 3 5 1│ └~──────────┘
⍋
once is what order the elements would be in when sorted and ⍋
twice is the indices that each element would go to.
Dyadic ⍋
is for character arrays only, and it grades as if the left argument was the alphabet:
{⍵['aeioubcdfghjklmnpqrstvwxyz'⍋⍵]}'helloworld'
┌→─────────┐ │eoodhlllrw│ └──────────┘
If characters are missing from the alphabet, they will be considered after the alphabet, and equivalent:
'abcdefgh'⍋'hawl'
┌→──────┐ │2 1 3 4│ └~──────┘
Dyadic ⍋
can also use multiple levels of sorting:
⍉↑'aeiou' 'bcdfghjklmnpqrstvwxyz'
┌→─┐ ↓ab│ │ec│ │id│ │of│ │ug│ │ h│ │ j│ │ k│ │ l│ │ m│ │ n│ │ p│ │ q│ │ r│ │ s│ │ t│ │ v│ │ w│ │ x│ │ y│ │ z│ └──┘
This 2D “alphabet” means that all vowels should come before all consonants, and only if otherwise the same, the vertical order will be considered.
{⍵[(⍉↑'aeiou' 'bcdfghjklmnpqrstvwxyz')⍋⍵]}'helloworld'
┌→─────────┐ │eoodhlllrw│ └──────────┘
This sorted all vowels before all consonants, and only then did it sort the vowels and the consonants. You can have up to 15 levels of sorting using this. If a letter occurs more than once, then its first occurrence rules. This is useful to fill gaps in (e.g.) columns of unequal height.
There is also ⍒
, which is grade down, which follows the pattern of ⍋
, but sorts the other way.