]box on -style=max -trains=tree -fns=on
┌→─────────────────────────────────────┐ │Was ON -style=max -trains=tree -fns=on│ └──────────────────────────────────────┘
Jeremy Howard
July 5, 2022
This notebook covers useful glyphs and concepts useful for the 2022 Dyalog APL competition. Be sure to read the Basics and Operators notebooks first.
]box on -style=max -trains=tree -fns=on
┌→─────────────────────────────────────┐ │Was ON -style=max -trains=tree -fns=on│ └──────────────────────────────────────┘
↑
(Up arrow)↑
(Mix)Mix takes elements of a complex array and stacks them along rows.
'Hip' 'Hop'
┌→────────────┐ │ ┌→──┐ ┌→──┐ │ │ │Hip│ │Hop│ │ │ └───┘ └───┘ │ └∊────────────┘
↑ 'Hip' 'Hop'
┌→──┐ ↓Hip│ │Hop│ └───┘
⍴↑ 'Hip' 'Hop'
┌→──┐ │2 3│ └~──┘
If elements are not all the same length, Mix will pad as needed
'Hip' 'Hop' 'Dance'
┌→────────────────────┐ │ ┌→──┐ ┌→──┐ ┌→────┐ │ │ │Hip│ │Hop│ │Dance│ │ │ └───┘ └───┘ └─────┘ │ └∊────────────────────┘
↑ 'Hip' 'Hop' 'Dance'
┌→────┐ ↓Hip │ │Hop │ │Dance│ └─────┘
⍴↑ 'Hip' 'Hop' 'Dance'
┌→──┐ │3 5│ └~──┘
Arrays can be mixed arrays containing both simple scalars and nested arrays
(6 4) 5 3
↑ (6 4) 5 3
┌→──────────┐ │ ┌→──┐ │ │ │6 4│ 5 3 │ │ └~──┘ │ └∊──────────┘
┌→──┐ ↓6 4│ │5 0│ │3 0│ └~──┘
(1 2) 3 (4 5 6)
↑(1 2) 3 (4 5 6)
┌→────────────────┐ │ ┌→──┐ ┌→────┐ │ │ │1 2│ 3 │4 5 6│ │ │ └~──┘ └~────┘ │ └∊────────────────┘
┌→────┐ ↓1 2 0│ │3 0 0│ │4 5 6│ └~────┘
↑
(Take)If ⍵
(right argument) is positive, returns the first ⍵
(right argument) elements of ⍺
(left argument)
4 ↑ 'Pineapple'
┌→───┐ │Pine│ └────┘
If ⍺
(left argument) is longer than the length of ⍵
(right argument), ⍵
is padded with blanks
12 ↑ 'Pineapple'
┌→───────────┐ │Pineapple │ └────────────┘
If ⍵
(right argument) is negative, it returns the last elements rather than the first elements.
¯5 ↑ 'Pineapple'
┌→────┐ │apple│ └─────┘
When ⍺
(left argument) is a scalar ⍵
(right argument) indexes on the first axis. The same rules apply to padding and negative numbers with higher dimension arrays.
⎕←mat←3 4⍴⍳12
┌→─────────┐ ↓1 2 3 4│ │5 6 7 8│ │9 10 11 12│ └~─────────┘
2 ↑ mat
┌→──────┐ ↓1 2 3 4│ │5 6 7 8│ └~──────┘
¯2 ↑ mat
┌→─────────┐ ↓5 6 7 8│ │9 10 11 12│ └~─────────┘
5 ↑ mat
┌→─────────┐ ↓1 2 3 4│ │5 6 7 8│ │9 10 11 12│ │0 0 0 0│ │0 0 0 0│ └~─────────┘
When an array is passed to ⍺
(left argument) you can slice on more than just the first axis. For example, 2 ¯3 ↑ mat
will give the first 2 rows (1st axis) and the last 3 columns (2nd axis) of mat.
2 ¯3 ↑ mat
┌→────┐ ↓2 3 4│ │6 7 8│ └~────┘
↓
(Down arrow)↓
(Split)Split takes an array and reshapes it to be all 1 row. That row will contain nested arrays that each contain the values in 1 of the rows.
⎕←mat←3 4⍴⍳12
┌→─────────┐ ↓1 2 3 4│ │5 6 7 8│ │9 10 11 12│ └~─────────┘
↓mat
┌→─────────────────────────────────┐ │ ┌→──────┐ ┌→──────┐ ┌→─────────┐ │ │ │1 2 3 4│ │5 6 7 8│ │9 10 11 12│ │ │ └~──────┘ └~──────┘ └~─────────┘ │ └∊─────────────────────────────────┘
⎕ ← mat ← 3 1⍴'Hip' 'Hop' 'Dance'
┌→────────┐ ↓ ┌→──┐ │ │ │Hip│ │ │ └───┘ │ │ ┌→──┐ │ │ │Hop│ │ │ └───┘ │ │ ┌→────┐ │ │ │Dance│ │ │ └─────┘ │ └∊────────┘
↓mat
┌→────────────────────────────────┐ │ ┌→──────┐ ┌→──────┐ ┌→────────┐ │ │ │ ┌→──┐ │ │ ┌→──┐ │ │ ┌→────┐ │ │ │ │ │Hip│ │ │ │Hop│ │ │ │Dance│ │ │ │ │ └───┘ │ │ └───┘ │ │ └─────┘ │ │ │ └∊──────┘ └∊──────┘ └∊────────┘ │ └∊────────────────────────────────┘
↓
(Drop)If ⍵
(right argument) is positive, removes the first ⍵
(right argument) elements of ⍺
(left argument).
4 ↓ 'Pineapple'
┌→────┐ │apple│ └─────┘
12 ↓ 'Pineapple'
┌⊖┐ │ │ └─┘
If ⍵
is negative, it removes the last elements rather than the first elements.
¯5 ↓ 'Pineapple'
┌→───┐ │Pine│ └────┘
When ⍺
(left argument) is a scalar ⍵
(right argument) indexes on the first axis. The same rules apply to padding and negative numbers with higher dimension arrays.
⎕←mat←3 4⍴⍳12
┌→─────────┐ ↓1 2 3 4│ │5 6 7 8│ │9 10 11 12│ └~─────────┘
2 ↓ mat
┌→─────────┐ ↓9 10 11 12│ └~─────────┘
When an array is passed to ⍺
(left argument) you can slice on more than just the first axis. For example, 2 ¯3 ↑ mat
will remove the first 2 rows (1st axis) and the last 3 columns (2nd axis) of mat.
2 ¯3 ↓ mat
┌→┐ ↓9│ └~┘
⌸
(Quad equal)⌸
(Key operator)The key operator
gives the index locations of each unique element in a vector. For example ,⌸ 'banana'
has a b
as the 1st element. a
is the 2nd, 4th, and 6th elements. n
is the 3rd and 5th elements.
a ← 'banana'
,⌸ a
┌→──────┐ ↓b 1 │ │a 2 4 6│ │n 3 5 │ └+──────┘
You can also combine this with other functions to do operations such as sum those index locations together.
a {⍺,+/⍵}⌸ ⍳6
┌→───┐ ↓b 1│ │a 12│ │n 8│ └+───┘
a ,⌸ ⍳6
┌→──────┐ ↓b 1 │ │a 2 4 6│ │n 3 5 │ └+──────┘
⊂
(Left shoe)⊂
(Enclose)Enclose is used to turn an array into a nested array with just one element. For example 1 2 3
is an array of shape 3.
1 2 3
┌→────┐ │1 2 3│ └~────┘
⍴1 2 3
┌→┐ │3│ └~┘
When we enclose 1 2 3
it becomes a scalar, meaning the shape is the empty list.
⊂1 2 3
┌─────────┐ │ ┌→────┐ │ │ │1 2 3│ │ │ └~────┘ │ └∊────────┘
⍴⊂1 2 3
┌⊖┐ │0│ └~┘
This can be used in conjunction with ,
in order to created arrays with nested elements.
1(2 3)
┌→────────┐ │ ┌→──┐ │ │ 1 │2 3│ │ │ └~──┘ │ └∊────────┘
⍴ 1(2 3)
┌→┐ │2│ └~┘
1,⊂2 3
┌→────────┐ │ ┌→──┐ │ │ 1 │2 3│ │ │ └~──┘ │ └∊────────┘
⊂ 1(2 3)
┌─────────────┐ │ ┌→────────┐ │ │ │ ┌→──┐ │ │ │ │ 1 │2 3│ │ │ │ │ └~──┘ │ │ │ └∊────────┘ │ └∊────────────┘
⍴⊂ 1(2 3)
┌⊖┐ │0│ └~┘
A good way to see what enclose does is to do multiple of them, which continues to nest your original array deeper the more you add.
⊂⊂ 1(2 3)
┌─────────────────┐ │ ┌─────────────┐ │ │ │ ┌→────────┐ │ │ │ │ │ ┌→──┐ │ │ │ │ │ │ 1 │2 3│ │ │ │ │ │ │ └~──┘ │ │ │ │ │ └∊────────┘ │ │ │ └∊────────────┘ │ └∊────────────────┘
An enclosed simple scalar is still the original simple scalar.
⊂1
1
This adds 4 5 6
to each element of the LHS (1, 2, and 3):
1 2 3+⊂4 5 6
┌→────────────────────────┐ │ ┌→────┐ ┌→────┐ ┌→────┐ │ │ │5 6 7│ │6 7 8│ │7 8 9│ │ │ └~────┘ └~────┘ └~────┘ │ └∊────────────────────────┘
It’s basically the same idea as this, since (⊂ 4 5 6)
and 1
are both scalars:
1 2 3+1
┌→────┐ │2 3 4│ └~────┘
⊂
(Partitioned enclose)partitioned enclose
is used to split an array into separate enclosures (created nested arrays).
⍺
(left argument) dictates where to split the array up. For example, 1 0 1 0 0 0 0
has a positive integer in the 1st and 3rd position and so we will split at the 1st and 3rd element
1 0 1 0 0 0 0⊂ 1 2 3 4 5 6 7
┌→──────────────────┐ │ ┌→──┐ ┌→────────┐ │ │ │1 2│ │3 4 5 6 7│ │ │ └~──┘ └~────────┘ │ └∊──────────────────┘
This can also be done with textual arrays
1 0 1 0 0 0 0⊂'HiEarth'
┌→─────────────┐ │ ┌→─┐ ┌→────┐ │ │ │Hi│ │Earth│ │ │ └──┘ └─────┘ │ └∊─────────────┘
The left argument of partitioned enclose
controls the positioning of the newly created enclosures.
2 0 1 0 3 0 ⊂ 1 2 3 4 5 6
┌→──────────────────────────────┐ │ ┌⊖┐ ┌→──┐ ┌→──┐ ┌⊖┐ ┌⊖┐ ┌→──┐ │ │ │0│ │1 2│ │3 4│ │0│ │0│ │5 6│ │ │ └~┘ └~──┘ └~──┘ └~┘ └~┘ └~──┘ │ └∊──────────────────────────────┘
1 0 2 0 0 0 0⊂'HiEarth'
┌→─────────────────┐ │ ┌→─┐ ┌⊖┐ ┌→────┐ │ │ │Hi│ │ │ │Earth│ │ │ └──┘ └─┘ └─────┘ │ └∊─────────────────┘
⊆
(Left shoe underbar)⊆
(Nest)(⊆1) ≡ ⊂1
1
(⊆1 2 3) ≡ ⊂1 2 3
1
(⊆ 1 (1 2 3)) ≡ 1 (1 2 3)
1
⊆ 1 (1 2 3)
1 (1 2 3)
┌→──────────┐ │ ┌→────┐ │ │ 1 │1 2 3│ │ │ └~────┘ │ └∊──────────┘
┌→──────────┐ │ ┌→────┐ │ │ 1 │1 2 3│ │ │ └~────┘ │ └∊──────────┘
⊂ 1 (1 2 3)
┌───────────────┐ │ ┌→──────────┐ │ │ │ ┌→────┐ │ │ │ │ 1 │1 2 3│ │ │ │ │ └~────┘ │ │ │ └∊──────────┘ │ └∊──────────────┘
⊆
(Partition)Partition breaks an array (⍵
) up based on the numbers provided in the left argument (⍺
).
A new partition is created anytime the integer in the left argument changes. 0 means that element is not included in the output. For example:
1 1 1 0 1 1 1 0 1 1 1 1⊆'How are you?'
: + The first 3 elements are in the first partition because there are 3 of the same integers 1 1 1
.
+ The first space is excluded because it is the fourth element in the right argument and the fourth element in the left argument is 0. + The second partition starts on the 5th element because it’s a non-zero integer that is not the same as the previous number.
1 1 1 0 1 1 1 0 1 1 1 1⊆'How are you?'
┌→───────────────────┐ │ ┌→──┐ ┌→──┐ ┌→───┐ │ │ │How│ │are│ │you?│ │ │ └───┘ └───┘ └────┘ │ └∊───────────────────┘
1 1 2 2 2 2 2⊆'HiEarth'
┌→─────────────┐ │ ┌→─┐ ┌→────┐ │ │ │Hi│ │Earth│ │ │ └──┘ └─────┘ │ └∊─────────────┘
1 1 2 2 2 0 0⊆'HiEarth'
┌→───────────┐ │ ┌→─┐ ┌→──┐ │ │ │Hi│ │Ear│ │ │ └──┘ └───┘ │ └∊───────────┘
⊃
(Right shoe)⊃
(Disclose;First)Disclose removes the nesting around arrays. For example, in simple arrays disclose with reverse and enclose. This is because it is getting the first element in a list, and when an array is enclosed the first element is the whole original array.
a←1 2
⊂a
┌───────┐ │ ┌→──┐ │ │ │1 2│ │ │ └~──┘ │ └∊──────┘
a ≡ ⎕ ← ⊃⊂1 2
┌→──┐ │1 2│ └~──┘ 1
What disclose/first is doing is getting the first element in the array, regardless of whether it is nested or not.
⊃ 'Word'
W -
⊃ (1 (1 2) 3) 4
┌→──────────┐ │ ┌→──┐ │ │ 1 │1 2│ 3 │ │ └~──┘ │ └∊──────────┘
⊃ (1 2)(3 4 5)
┌→──┐ │1 2│ └~──┘
⊃
(Pick)Pick is a way of indexing into an array to get particular elements. For example 3 ⊃ 'Word'
will give the 3rd element in Word
.
3 ⊃ 'Word'
r -
If the element you are selecting is an array, that array will be returned
2 ⊃ (1 2)(3 4 5)
┌→────┐ │3 4 5│ └~────┘
If you pass an array to the left argument it will index further. For example, 2 1 ⊃ (1 2)(3 4 5)
will first go to the 2nd element (3 4 5
) and then output the first element inside of that (3
).
2 1 ⊃ (1 2)(3 4 5)
3
In order to index into a multi-dimensional array you need to specify the different dimensions as a scalar. For example if you are looking at a matrix (2d array), (⊂2 1)
as the left argument will output the value in the 2nd row, 1st column.
⎕←mat ← 2 3⍴⍳6
┌→────┐ ↓1 2 3│ │4 5 6│ └~────┘
(⊂2 1) ⊃ 2 3⍴⍳6
4
These above concepts of indexing into subarrays and indexing into multi-dimensional arrays can be combined in one function call.
G←2 3⍴('ABC' 1)('DEF' 2)('GHI' 3)('JKL' 4),('MNO' 5)('PQR' 6)
G
┌→────────────────────────────────────┐ ↓ ┌→────────┐ ┌→────────┐ ┌→────────┐ │ │ │ ┌→──┐ │ │ ┌→──┐ │ │ ┌→──┐ │ │ │ │ │ABC│ 1 │ │ │DEF│ 2 │ │ │GHI│ 3 │ │ │ │ └───┘ │ │ └───┘ │ │ └───┘ │ │ │ └∊────────┘ └∊────────┘ └∊────────┘ │ │ ┌→────────┐ ┌→────────┐ ┌→────────┐ │ │ │ ┌→──┐ │ │ ┌→──┐ │ │ ┌→──┐ │ │ │ │ │JKL│ 4 │ │ │MNO│ 5 │ │ │PQR│ 6 │ │ │ │ └───┘ │ │ └───┘ │ │ └───┘ │ │ │ └∊────────┘ └∊────────┘ └∊────────┘ │ └∊────────────────────────────────────┘
((⊂2 1),1) ⊃ G
┌→──┐ │JKL│ └───┘
⊢
(Right tack)⊢
(Same)Return the argument back unchanged
⊢1
1
⊢'abc'
┌→──┐ │abc│ └───┘
⊢
(Right)Returns the right argument back unchanged
'abc'⊢1
1
1⊢'abc'
┌→──┐ │abc│ └───┘
⊣
(Left tack)⊣
(Same)Returns the argument back unchanged
⊣'abc'
┌→──┐ │abc│ └───┘
⊣
(Left)Returns the left argument back unchanged
'abc'⊣1
┌→──┐ │abc│ └───┘
1⊣'abc'
1
⊥
(Up tack)⊥
(Decode)Decode decodes the right argument using the encode definition on the left.
In the example of 2 ⊥ 1 1 0 1
:
1 1 0 1
in binary is equal to 13, so decode will return 13.2 ⊥ 1 1 0 1 ⍝ binary decode
13
An array can also be decoded if each element is encoded differently. For example, if you have an array of 3 elements representing hours, minutes, and seconds you can use decode to convert it into seconds.
Hours would be decoded with 24, because there’s 24 hours in a day. Minutes would be decoded with 60, because there’s 60 minutes in an hour. Seconds would be decoded with 60 because there are 60 seconds in a minute.
⍝ mixed radix: conversion of hours,
⍝ minutes and seconds to seconds:
24 60 60 ⊥ 2 46 40
10000
(2×60×60) + (46×60) + 40
10000
Below are a few examples of a few other examples of ways decode can be used.
A decimal decode:
10 ⊥ 1 1 0 1 ⍝ decimal decode
1101
10 ⊥ 3 4 1 6 ⍝ decimal decode
3416
1j1⊥1 2 3 4
5J9
1⊥
is sum over the first axis (+⌿)
1⊥3 1 4 1 5 9
23
⎕←M←3 4⍴⍳12
┌→─────────┐ ↓1 2 3 4│ │5 6 7 8│ │9 10 11 12│ └~─────────┘
1⊥M
┌→──────────┐ │15 18 21 24│ └~──────────┘
⊤
(Down tack)⊤
(Encode)10 10 10 10 10 ⊤ 3658 ⍝ decimal encode
┌→────────┐ │0 3 6 5 8│ └~────────┘
10 10 10 ⊤ 3658 ⍝ truncated decimal encode
┌→────┐ │6 5 8│ └~────┘
0 10 10 ⊤ 3658
┌→─────┐ │36 5 8│ └~─────┘
2 2 2 2 ⊤ 7 ⍝ binary encode
┌→──────┐ │0 1 1 1│ └~──────┘
2 2 ⊤ 7 ⍝ truncated binary encode
┌→──┐ │1 1│ └~──┘
2 2 2 2 ⊤ 5 7 12 ⍝ binary encode
┌→────┐ ↓0 0 1│ │1 1 1│ │0 1 0│ │1 1 0│ └~────┘
⍝ mixed radix: encode of 10000 seconds
⍝ to hours, minutes and seconds:
24 60 60 ⊤ 10000
┌→──────┐ │2 46 40│ └~──────┘
In traditional mathematical notation (TMN): (f+g)(x)=f(x)+g(x)
. Forks (3-trains) are just a generalisation of this pattern to all functions (though the middle one has to be dyadic).
(÷3)+(*3)
20.41887026
(÷+*)3
20.41887026
(+/÷≢) 2 5 8 9
6
mean ← +/÷≢
mean 2 5 8 9
6
For a dyadic fork, each of f
and g
are passed the LHS and the RHS:
' '≠'How are you?'
┌→──────────────────────┐ │1 1 1 0 1 1 1 0 1 1 1 1│ └~──────────────────────┘
' '⊢'How are you?'
┌→───────────┐ │How are you?│ └────────────┘
(' '≠'How are you?') ⊆ (' '⊢'How are you?')
┌→───────────────────┐ │ ┌→──┐ ┌→──┐ ┌→───┐ │ │ │How│ │are│ │you?│ │ │ └───┘ └───┘ └────┘ │ └∊───────────────────┘
' '(≠⊆⊢)'How are you?'
┌→───────────────────┐ │ ┌→──┐ ┌→──┐ ┌→───┐ │ │ │How│ │are│ │you?│ │ │ └───┘ └───┘ └────┘ │ └∊───────────────────┘
split ← {(⍺≠⍵) ⊆ (⍺⊢⍵)}
' ' split 'How are you?'
┌→───────────────────┐ │ ┌→──┐ ┌→──┐ ┌→───┐ │ │ │How│ │are│ │you?│ │ │ └───┘ └───┘ └────┘ │ └∊───────────────────┘