APL Competition 2022

Dyalog
APL
Glyphs
Author

Jeremy Howard

Published

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│
└──────────────────────────────────────┘

Glpyhs

(Up arrow)

monadic (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│
└~────┘

dyadic (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)

monadic (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│ │ │
│ │ └───┘ │ │ └───┘ │ │ └─────┘ │ │
│ └∊──────┘ └∊──────┘ └∊────────┘ │
└∊────────────────────────────────┘

dyadic (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)

monadic (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)

monadic (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│
└~────┘

dyadic (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)

monadic (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│ │ │
│ │   └~────┘ │ │
│ └∊──────────┘ │
└∊──────────────┘

dyadic (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)

monadic (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│
└~──┘

dyadic (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)

monadic (Same)

Return the argument back unchanged

⊢1
 
1
 
⊢'abc'
┌→──┐
│abc│
└───┘

dyadic (Right)

Returns the right argument back unchanged

'abc'⊢1
 
1
 
1⊢'abc'
┌→──┐
│abc│
└───┘

(Left tack)

monadic (Same)

Returns the argument back unchanged

⊣'abc'
┌→──┐
│abc│
└───┘

dyadic (Left)

Returns the left argument back unchanged

'abc'⊣1
┌→──┐
│abc│
└───┘
1⊣'abc'
 
1
 

(Up tack)

dyadic (Decode)

Decode decodes the right argument using the encode definition on the left.

In the example of 2 ⊥ 1 1 0 1:

  • 2 on the left means that all values are encoded with 2 values, in other words binary
  • 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)

dyadic (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│
└~──────┘

Forks

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?│ │
│ └───┘ └───┘ └────┘ │
└∊───────────────────┘