Discovery
To practise our skill some more, let us find in our variable Val the positions of numbers greater than 35.
Here are the stages of our journey:
Val
22 37 41 19 54 11 34
Val>35
0 1 1 0 1 0 0
⍴Val
7
⍳⍴Val
1 2 3 4 5 6 7
Let us compare two of these results:
Val>35
0 1 1 0 1 0 0
⍳⍴Val
1 2 3 4 5 6 7
One sees that if one eliminates (by a compression) the terms which correspond to zeros in order to retain those corresponding to 1, one easily gets the positions required: 2 3 5. Thus the job may be done as follows:
(Val>35)/⍳⍴Val
2 3 5
This expression applies in many different situations.
Here is a similar use, but applied to text data: to find the positions of ‘a’ within a phrase; the method is the same.
Phrase ← 'The Argentinian tango is not in fashion'
(Phrase='a')/⍳⍴Phrase
14 18 34
Keep it Dark!
Proudly having found all the ‘a’s, we may wish to find all the vowels.
Alas, although we can write (Phrase='a'), because a vector can be compared with a single value, one cannot write (Phrase='aeiou'), because that would require the item by item comparison of a phrase of 39 letters and 'aeiou' which has only 5.
Well, one may compare 39 letters with 39 other letters, or compare them with one only, but not with 5.
So we must have recourse to a new function: membership denoted ∊, also used in mathematics.
The expression A∊B shows, by a binary result, which elements of the variable A appear (wherever they may be) in the variable B. And that works no matter what the shapes, dimensions or type of data of A and B, a small marvel!
For example:
5 7 2 8 4 9 ∊ 3 4 5 6
1 0 0 0 1 0
'pissenlit'∊'jardin'
0 1 0 0 0 1 0 1 0
(Only the ‘i’s and the ‘n’ appear in ‘jardin’.) So in pursuit of our enquiry we shall write:
(Phrase ∊ 'aeiou')/⍳⍴Phrase
3 8 11 13 14 18 21 23 27 30 34 37 38
One can also use membership between a vector and a table (matrix), as shown below (the list of towns is a variable created earlier).
Towns
Martigues
Paris
Strasbourg
Granville
Nantes
Fréjus
Towns ∊ 'aeiouy'
0 1 0 0 1 0 1 1 0 0 0
0 1 0 1 0 0 0 0 0 0 0
0 0 0 1 0 0 1 1 0 0 0
0 0 1 0 0 1 0 0 1 0 0
0 1 0 0 1 0 0 0 0 0 0
0 0 0 0 0 1 0 0 0 0 0
Note that the result has always the same shape as the data at the left:
'aeiouy' ∊ Towns
1 1 1 1 1 0
None of the towns contains a ‘y’.
Making a Point
Programmers have often said to me that the symbol ∊ constitutes for them the one irrefutable proof that APL is an advanced mathematical language, not suitable for all users. I am inclined to agree this is the general view, are you?
Even if it has only been used in education since the beginning of the 60s, this symbol has appeared (if I am not mistaken) as part of the apparatus of “modern” mathematics since the second half of the 19th century. Knowing that we are in the 21st century, we can say that it is at least 100 years old.
What is more, when my elder son was 11 years old, membership was taught to Year 7 mathematics classes. I can say, having followed his learning closely, that it did not pose any greater difficulty than if that lesson had been delayed until much later.
In other words, those learned programmers who find membership too difficult to understand are unreasonably claiming that things which have been within the powers of a child of 11 for the past 100 years, have suddenly become too advanced and difficult.
In these circumstances I see that one cannot put APL into anybody’s hands; in particular those of these programmers. But is this a fair criticism of APL?
A Generally Powerful Function
We have a very useful method to look for the positions of letters or numbers in a vector, but it has some small problems we have not yet covered. There is another way, which uses the dyadic form of the symbol ⍳ (iota).
Vec ← 15 40 63 18 27 40 33 29 40 88 ⍝ vector to search
Vec ⍳ 29 63 40 33 50 ⍝ values sought
8 3 2 7 11
It is the case that 29, 63, 40 and 33, occur respectively in positions 8, 3, 2 and 7.
The first surprise: the value 40 occurs three times in Vec, but only the first occurrence is mentioned. If the response for each value sought has to be a position; how may one, looking for five numbers, obtain seven results?
Second surprise: the value 50 assigned position 11 … in a vector comprising only ten items! This is how the function index of (dyadic ⍳) reports that a value is absent.
At first sight that seems strange but in fact it is the characteristic which makes this function so generally powerful.
An Example
A motor manufacturer decides he will offer his customers a discount on the catalogue price. (Now you know that this example is imaginary!)
The discount rate will depend on the geographic area according to the following table:
Area Discount
17 9%
59 6%
84 5%
89 4%
Other 2%
The problem is to calculate the discount rate which may be claimed for a potential customer who lives in area D; for example D←84.
Let us begin by creating two variables:
AREA ← 17 50 59 84 89
DISCT ← 9 8 6 5 4 2
Let us see if 84 is in the list of favoured areas:
AREA⍳D
4
84 is all right: the 4th item in the list. Let us find the current rate of discount for this index position:
DISCT[4]
5
This customer can claim a 5% discount; that’s good. One may simply write: DISCT[AREA⍳D] .
If a customer lives in any area such as 75, 45, or 93, the expression AREA⍳D will give in all cases the response 6, and DISCT[6] will always find the rate 2%, as intended.
The importance of this approach is that it is vector-based. Suppose that publicity attracts crowds and that therefore D is no longer a scalar but a vector, the solution is still valid:
D←24 75 89 60 92 50 51 50 84 66 17 89
DISCT[AREA⍳D]
2 2 4 2 2 8 2 8 5 2 9 4
All that without a program, neither “loop” nor “test”; readers who know other programming languages will have no difficulty in making the comparison.
Share with your friends: |