Finding and Replacing Values

Searching and finding

It is common to make multiple equality comparisons. Doing this one at a time becomes tedious.

      text ← 'This is my sentence'
(text='a')∨(text='e')∨(text='i')∨(text='o')∨(text='u')
0 0 1 0 0 1 0 0 0 0 0 0 1 0 0 1 0 0 1

The membership function returns a Boolean array (1s and 0s) of the same shape as ⍺ where 1 indicates the location of any of the elements in ⍵.

      text∊'aeiou'
0 0 1 0 0 1 0 0 0 0 0 0 1 0 0 1 0 0 1

      text ← 2 6⍴'I LIKE APL  '
text∊'AEIOU'
1 0 0 1 0 1
0 1 0 0 0 0

Find ⍺⍷⍵ will give a 1 indicating the location of the first element of ⍺ when the entire array ⍺ is found as a subarray in ⍵.

      y ← 2 10⍴'APPLESLESSMOTIONSLOW'
'LESS'⍷y
0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0

      'LESION'⍷y
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0

      (2 3⍴'LESION')⍷y
0 0 0 1 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0

Index of ⍺⍳⍵ will return the index in ⍺ where ⍵ is found as a major cell.

      text ← 2 3 4⍴'SOME APPLES'
text∊'LESS'
1 0 0 1
0 0 0 0
1 1 1 1

0 0 1 0
0 0 0 1
1 1 1 0

      'LESS'⍷text
0 0 0 0
0 0 0 0
1 0 0 0

0 0 0 0
0 0 0 0
0 0 0 0

      (1⌷text)⍳'LESS'
3

For subarrays not found in ⍺, index-of returns 1+≢⍺.

      'keep'⍳'qwert'
5 5 2 5 5

Sorting and grouping

To sort, index by the grade.

      Sort←{(⊂⍋⍵)⌷⍵}
Sort 'the alphabet'
 aabeehhlptt

Grouping is an incredibly common operation when handling data. The python "dataframe" framework Pandas has a groupby function and anybody who has used SQL is likely to be familiar with this idea.

The key operator applies its operand function to groups of major cells corresponding to the unique major cells in ⍵. For a vector, this is the unique list of elements.

In the monadic case, ⍵ is a list of indices.

      {⍺,⊂⍵}⌸'mississippi'
┌─┬────────┐
│m│1       │
├─┼────────┤
│i│2 5 8 11│
├─┼────────┤
│s│3 4 6 7 │
├─┼────────┤
│p│9 10    │
└─┴────────┘

In the dyadic case, it is a list of keys which are provided as ⍺.

      ↑'ABBDDDCDBAA' 'mississippi'
ABBDDDCDBAA
mississippi

'ABBDDDCDBAA'{⍺,⊂⍵}⌸'mississippi'
┌─┬────┐
│A│mpi │
├─┼────┤
│B│isp │
├─┼────┤
│D│sisi│
├─┼────┤
│C│s   │
└─┴────┘

Interval index is a function for classifying data by boundaries.

See if you can write the Grade function from problem set 3, problem 6 using interval index ⍺⍸⍵.

Grade ← {'FDCBA'[0 65 70 80 90⍸⍵]}

Set functions

Those familiar with set theory from traditional mathematics will recognise the following symbols. The APL functions are closely related to their set theory counterparts.

The union of two vectors contains elements from ⍺ catenated with elements in ⍵ not found in ⍺.

      'WASH' ∪ 'SHOUT'
WASHOUT

The intersection contains elements only found in both.

      'WASH' ∩ 'SHOUT'
SH

The function without removes elements from ⍺ which are found in ⍵.

The set difference is then neatly expressed as a fork of the union without the intersection.

      'WASH' (∪~∩) 'SHOUT'
WAOUT

Assigning to arrays

Indexed Assignment

Assign values at specified indices.

t←4 4⍴'some sample text'
t[⍸t∊'aeiou']←'!'

Selective Assignment

Most ways of selecting from arrays can be used to change values in an array. Here is an example using compress:

      a ← 2 3⍴⎕A
(1 0⌿a)←'⌺'
a
⌺⌺⌺
DEF

Modified Assignment

Experiment with the following expressions, paying particular attention to the name f← array construct.

salaries←18250 42500 56000 57250 48640
codes←'ACDDC'
salaries×←1.1
salaries[⍸codes='C']×←1.0

a←⎕A
(3↑a),←'abcd'

The At operator

Monadic functions take a single right argument array as input. Dyadic functions take two argument arrays.

Monadic operators take a single left operand which can be a function or an array (as in +/ where plus + is the function operand and reduce / is the operator).

Dyadic operators take two operands which could be functions or arrays depending on the operator's definition. For example, the rank operator F⍤k takes a function left operand F and array right operand vector k of up to 3 elements.

Selective and indexed assignment methods will change the values of variables. The "at" operator @ merges two arrays at specified indices and returns a new array.

If a function right operand returns a boolean array when applied to ⍵ (e.g. 3=1 3 5) then ones 1 in the boolean array determine where scalars of ⍺ are inserted.

      ('∆⍥'@{⍵∊'AEIOU'})2 3⍴'DYALOG'
DY∆
L⍥G

      (' '@2 3 4)'DYALOG'
D   OG

      (' '@(1 2)(1 3)(2 1))2 3⍴'DYALOG'
D
OG

Generally, the left operand to @ is a function applied to scalars in ⍵ which are specified by a right operand that is either an array of scalar (simple or enclosed vector) indices or a boolean array returned by a right operand function. An array left operand is shorthand for a constant function that returns the array.

      Upper ← 1∘⎕C
My Excellent Heading

Strand Assignment

Distributed assignment or strand assignment allows multiple names to be defined using a single assignment arrow ←.

      (max min avg)←{(⌈⌿⍵)(⌊⌿⍵)((+⌿÷≢)⍵)}3 1 4 1 5

Note

Strand assignment does not require names to be parenthesised, but we strongly recommend it for clarity.

We can assign items in nest to the three variables s←'A' v←1 2 3 and m←3 3⍴⍳9 using a single assignment arrow.

      nest←('A'(1 2 3))(3 3⍴⍳9)
((s v) m)←nest

Warning

You might have some issues when using inline, modified or strand assignment in dfns. This is by design, but can be a source of confusion.

      { a←3 ⋄ f←+ ⋄ a f←3 ⋄ a }⍬
3
a←3 ⋄ f←+ ⋄ a f←3 ⋄ a
6

You can get around these problems by writing ∘⊢ to the immediate right of any function involved:

      { a←3 ⋄ f←+ ⋄ a f∘⊢←3 ⋄ a }⍬
6

You might find it best to simply keep the modification explicit:

      { a←3 ⋄ f←+ ⋄ a←a+3 ⋄ a }⍬
6

Problem set 6

1. Write a function test if there are any vowels 'aeiou' in text vector ⍵

      AnyVowels 'this text is made of characters'
1

      AnyVowels 'bgxkz'
0

We can use membership to see which elements of our argument belong to the set 'aeiou'. Then we can then ask if there are any 1s in the Boolean vector:

AnyVowels ← {∨/⍵∊'aeiou'}

An easy eay to check for any ones in a high rank Boolean array is to use membership:

AnyVowels ← {1∊⍵∊'aeiou'}
2. Write a function to count the number of vowels in its character vector argument ⍵

      CountVowels 'this text is made of characters'
9

      CountVowels 'we have twelve vowels in this sentence'
12

Counting the 1s in the Boolean result of membership ⍺∊⍵ counts the vowels.

CountVowels ← {+/⍵∊'aeiou'}
3. Write a function FoundIn which accepts a nested scalar or vector of character vectors and returns a 1 where each vector contains letters in the simple character vector ⍺.

      'ei' FoundIn 'Katie' 'Bob' 'Stephen' 'Jessica' 'Andy'
1 0 1 1 0

One solution is to bind ⍺ to the membership function (∊∘⍺) to form a monadic function "contains alpha" which can be applied on each vector in our nested argument.

FoundIn ← {∨/¨∊∘⍺¨⍵}
4. Write a function Clean that changes all non-digits into stars.

      Clean 'Easy as 1, 2 and 3'
********1**2*****3

      Clean '1000'
1000

      Clean 'APL works!'
**********

We cannot assign to ⍵ in a dfn, so we must create an intermediate variable name:

 Clean ← {
r←⍵ ⋄ d←~⍵∊⎕D
(d/r)←'*'
r
}

We can provide a function right operand to the at operator which checks for non-digits:

Clean ← '*'@(~∊∘⎕D)
5. The following expression contains an error:

      ('∆⍥'@1)2 3⍴'DYALOG'
LENGTH ERROR
('∆⍥'@1)2 3⍴'DYALOG'
∧

Change the parenthesised function containing @ so that it gives the following results:

1. ∆∆∆
LOG
2. ∆∆∆
⍥⍥⍥

1.       ('∆'@1)2 3⍴'DYALOG'
∆∆∆
LOG

2.       ((2 3⍴3/'∆⍥')@1 2)2 3⍴'DYALOG'
∆∆∆
⍥⍥⍥

6. Create a function ReplaceHead which returns its left argument vector ⍺, but with the first ⍴⍵ elements replaced with the contents of ⍵.

      'apple' ReplaceHead 'Eat'
Eatle

      'apple' ReplaceHead 'rang'
range

      'apple' ReplaceHead 'ENTERPRISE'
ENTER

This solution uses indexed assignment:

ReplaceHead ← {r←⍺ ⋄ s←(≢⍺)⌊≢⍵ ⋄ r[⍳s]←s↑⍵ ⋄ r}

This solution uses the over operator F⍥G to express the minimum length of ⍺ and ⍵. It then uses the at operator to do the substitution.

ReplaceHead ← {s←⍺⌊⍥≢⍵ ⋄ (s↑⍵)@(⍳s)⊢⍺}
7. Bus stops in a town are labelled A to E. Define a function RouteMatrix which returns a Boolean matrix where 1s indicate that buses go from one bus stop to the next.

      RouteMatrix 'BE' 'C' 'AE' 'BCE' 'A'
0 0 1 0 1
1 0 0 1 0
0 1 0 1 0
0 0 0 0 0
1 0 1 1 0

      'ABCDE'⍪RouteMatrix 'C' 'CDE' 'ABDE' 'E' 'B'
A B C D E
0 0 1 0 0
0 0 1 0 1
1 1 0 0 0
0 1 1 0 0
0 1 1 1 0

RouteMatrix ← {'ABCDE'∘.∊⍵}