]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.
⊂11
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) ≡ ⊂11
(⊆1 2 3) ≡ ⊂1 2 31
(⊆ 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⍴⍳64
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
⊢11
⊢'abc'┌→──┐ │abc│ └───┘
⊢ (Right)Returns the right argument back unchanged
'abc'⊢11
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 decode13
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 45J9
1⊥is sum over the first axis (+⌿)
1⊥3 1 4 1 5 923
⎕←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 96
mean ← +/÷≢mean 2 5 8 96
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?│ │ │ └───┘ └───┘ └────┘ │ └∊───────────────────┘