Introduction - Will You Play APL With Me?
Contents
1. Introduction - Will You Play APL With Me?#
1.1. Will You Follow Us?#
We would like to have you discover a new land, a land where people who may or may not be specialists in programming can process their data, build computerised applications, and take pleasure in using a programming language which is an extremely elegant and powerful tool of thought.
1.1.1. Beware: Dyalog APL is Addictive!#
Among the hundreds of programming languages which have been created, most of them share the same fundamentals, the same basic instruction set, approximately the same functions, and by and large the same methods to control the logic of a program. This greatly influences the way people imagine and build solutions to computing problems. Because the languages are so similar, the solutions are similar. Does it mean that these are the only ways of solving problems? Of course not!
Dyalog APL is there to open doors, windows, and minds, prove that original new methods do exist, and that mathematics is not limited to four basic operations. Using APL will expand and extend the range of mental models that you use to solve problems, but beware: once you are hooked on APL, there is a real risk that you will no longer accept the limitations of “traditional” programming languages.
1.1.2. Setup#
If you are reading this book online, chances are you can make the most of the interactivity provided by the book format: code snippets can be run by you and you can invent your own data and calculations by modifying those same snippets.
If you are reading this book in a non-interactive format, you can experiment on your own computer by installing Dyalog APL, which you can download from here. You can refer to the Getting Started chapter for installation help. If you do not wish to install Dyalog APL right away you can also follow along online at TryAPL, where a large subset of APL is supported.
If none of these options suit you, you should still be able to gain an appreciation of the language from these pages and, we hope, enjoy the experience.
As you will see in the following pages, APL uses special symbols, like ⍉
, ⍴
, and ↓
, which you can enter in a variety of ways:
you can install a special APL keyboard layout, which will work everywhere. Dyalog APL comes with the option of installing such a layout;
you can use the APL language bar, which works on your browser;
you can use the software’s built-in language bar, which relies on the software having a language bar. The Dyalog APL interpreter and TryAPL have such a built-in language bar.
The snippet below uses some APL incantations to display the US keyboard layout, and other layouts (as well as Mac layouts) can be found here.
)copy dfns notes.keyboards
18↑k↓⍨¯1+⊃⊃⍸'APL/en-US keyboard'⍷k←⎕fmt keyboards
Here is how the keyboard is to be used:
all the standard English letters, numerals and symbols are typed as usual; and
the majority of the APL symbols are obtained by pressing the APL key, in conjunction with another key. The APL key is the key you use to access the symbols on the right half of the keycaps above; the APL key is system dependent and is usually one of Ctrl, Alt or AltGr (or right Alt):
the symbols on the bottom-right corner of the keycap are accessed by pressing the APL key, for example APL+r gives
⍴
and APL+e gives∊
;if you look at the E key in the figure above, you can see that the symbol
⍷
appears on top of the symbol∊
. To type the symbols on the top-right corner of a keycap, one uses the APL+Shift combo, so that APL+Shift+E types an⍷
.
You may notice that some symbols appear twice on the keyboard. This is the case, for example, for the symbols <
=
>
. These symbols are all part of a normal keyboard but they have been repeated on the APL keyboard, mostly in order to group the symbols used for comparison functions together (look at the bottom-right corner of the keycaps for the numbers 3 to 8). Do not worry: no matter which key you use to produce one of the duplicated symbols, you’ll obtain the same result.
1.2. Our First Steps into APL’s Magic World#
1.2.1. The APL Interpreter Session and Jupyter Notebook Inputs#
If you installed Dyalog APL or you intend to follow along using TryAPL, you will see that what you type starts six characters right from the left margin (we say it is “indented”), whereas the computer’s response begins at the left margin. This would look something like this:
3 + 3
6
If you type 3 + 3
on your Dyalog APL interpreter session, you can evaluate it by just pressing Enter to obtain the answer 6
.
However, this book was written in Jupyter notebooks, which obey a different format:
what the user types goes in a grey rectangle that is preceded by a
In [X]:
annotation that you evaluate by pressing Ctrl+Enter; andthe computer’s response goes in a white rectangle that is preceded by the corresponding
Out[X]:
annotation.
Using the same example as above, it looks like this:
3 + 3
1.2.2. Simple Operations#
Let us try some more simple examples with the four basic arithmetic operations:
27 + 53
1271 - 708
86 ÷ 4 ⍝ The "divide" sign is obtained using APL+=
59 × 8 ⍝ The "multiply" sign is obtained using APL+-
You will notice that in the examples given in the book we very often put a blank space between a symbol and the surrounding names or values. This is unnecessary in most cases and we only do so in order to improve readability. Later on, we will gradually cease to insert the blank spaces in expressions that you should become familiar with along the way.
You can see that APL behaves like any hand-held calculator with, however, a small difference: multiplication is represented by the multiplication symbol (×
) which is used in schools in many countries; likewise for division (÷
).
In most other computer languages, a star *
is used for multiplication and /
for division. This is a legacy of the early days of computers, when the character set was limited to the one available on a typewriter. At the time it was decided to use *
and /
in place of ×
and ÷
. But it is now possible to display any type of symbol on a screen and on a printer, and this transposition is no longer justifiable. The use of common symbols, which are taught all over the world, aids the understanding of APL by non programmers.
If you are familiar with other programming languages, you may occasionally and erroneously use *
instead of ×
. Let’s see what might happen then:
7 * 3 ⍝ In APL the star means "power"
⍝ so that 7*3 is equivalent to 7×7×7
1.2.3. Variables#
As in any programming language, it is possible to create variables. Just choose a name and use the left arrow to assign it a value. In APL, a numeric value can consist of a single number, or several numbers separated by at least one blank space. The arrow can be obtained using APL+[ (if you have a non-UK/US layout and APL+[ did not work for you, refer to the keyboard layout above: the P is on the top right and ←
is to its right, so you might need to press the same physical key).
vat ← 19.6 ⍝ Read it as: vat gets 19.6
years ← 1952 1943 1986 2007
The names are “case sensitive”. It means that three variables named respectively VAT
, Vat
, and vat
, would be distinct, and may contain different values.
To ask for the contents of a variable, just type its name and evaluate it like you did for the previous examples:
vat
years
1.3. Array Processing#
APL is able to operate on two sets of numbers, provided those two sets have the same “shape”. For the moment, understand this as “the same number of items”. For example, suppose that you have a list of prices of five products, and the quantity bought of each:
Prices |
Quantities |
---|---|
5.20 |
2 |
11.50 |
1 |
3.60 |
3 |
4.00 |
6 |
8.45 |
2 |
You can create two variables like this:
price ← 5.2 11.5 3.6 4 8.45
qty ← 2 1 3 6 2
When multiplied together, the variables are multiplied item by item, and produce a result of the same length. That result can be assigned to a new variable.
costs ← price × qty
costs
This array processing capability eliminates most of the “loops” which are common to other programming languages. This remains true even if the data is not a simple list but a multi-dimensional array, of almost any size and number of dimensions.
To make it clear, imagine that a Sales Director makes forecasts for sales of four products over the coming six months, and assigns them to the variable forecast
. At the end of the six months, they assign the real values to the variable actual
. Here they are:
⎕RL ← 73
forecast ← 10×?4 6⍴55
forecast
⎕RL ← 73
actual ← forecast + ¯10+?4 6⍴20
actual
Remark
We initialise the forecast
and actual
variables with some random values by the use of the functions ⍴
and ?
and with a magic incantation ⎕RL
. You will learn more about ⍴
, ?
and ⎕RL
later on in the book.
It is clear that the first idea of any Sales Director will be to ask for the differences between what they expected and what they really got. This can be done easily by typing:
actual - forecast
Note that to distinguish the sign attached to negative values from subtraction, negative values are shown with a high minus sign. The high minus sign can be obtained by pressing APL+2.
In traditional procedural languages, the operation would probably require two nested loops or a similar construct, like the following pseudocode tries to illustrate:
for i in range(0, rows(actual)):
for j in range(0, cols(actual)):
diff[i, j] = actual[i, j] - forecast[i, j]
Even if this may seem obvious to a programmer, it is worth noting that most of the code has nothing to do with the user requirement. The only important thing (subtract forecasts from actual values) is hidden behind the detailed working of the computer program.
To have a calculation done by a machine, one must translate our human wording into something that the computer can understand. With traditional languages, most of the effort is made by the human, to produce a program like the pseudocode example above. The great advantage of APL is that the human has generally much less effort to make, and the machine does the rest.
We have seen that APL will work on two variables of the same shape; it also works if one of the variables is a single item, which is called a scalar. If so, the other variable may be of any shape.
For example, if we want to calculate the amount of 19.6% VAT applied to the variable price
above, we can type price × vat ÷ 100
(or vat × price ÷ 100
as well), as shown here:
price × vat ÷ 100
These prices would then require some rounding, but this is not very important right now.
1.4. More Symbols#
Most programming languages represent only a very small subset of the mathematical functions using symbols (typically +
, -
, *
and /
). The creator of APL, Kenneth E. Iverson, chose to include many traditional mathematical symbols in his language, and also added some new symbols to the set that we already know so well.
For example, many functions which in other programming languages are library routines with names like “maximum” have their own symbols in APL.
The function maximum (represented by ⌈
, typed with APL+s) returns the greater of two numbers, or of two arrays of numbers compared item by item.
There is also, as one might expect, a symbol for minimum (⌊
, typed with APL+d).
75.6 ⌈ 87.3
If we type more than one item on either side, we get comparison item by item. Later, we will see this is because maximum is a scalar function.
11 28 52 14 ⌈ 30 10 50 20
11 28 52 14 ⌊ 20
APL supports about 80 symbols. Since some symbols have more than one meaning one could argue at length about the exact number.
This is nothing to worry about: some of the symbols are familiar, such as ×
or >
or again ÷
and -
, but also !
and a good many others.
1.5. Most Symbols Have a Double Meaning#
This is not a peculiarity of APL; in algebra we are familiar with the use of symbols as common as the minus sign being used in two different ways.
In the expression \(a = x \color{red}{-} y\) the minus sign means subtract, whereas in \(a = \color{red}{-}y\) the minus sign indicates the negation of \(y\). The first form is called the dyadic use of the symbol. The second form is called the monadic use of the symbol.
It is the same in APL, where most of the symbols can have two meanings.
For example, to find the shape (the dimensions) of an array, one uses the Greek letter Rho (⍴
), which can be read “shape of …”, in its monadic use. It is produced using APL+r.
For example,
⍴ price
price
has 5 items, and forecast
has 4 rows and 6 columns:
⍴ forecast
Used dyadically, the same symbol will organize items into a specified shape. For example, suppose that we want to create the matrix below:
We must give the computer two pieces of information:
First the shape to give to the matrix:
4 2
(4 rows of 2 columns)Next the contents of the matrix:
25 60 33 47 11 44 53 28
It is the symbol ⍴
(Rho) which makes the connection between shape and the contents:
tab ← 4 2 ⍴ 25 60 33 47 11 44 53 28
tab
A new variable tab
is thereby created, and this is also how the variables forecast
and actual
above were made.
1.5.1. Conventions#
In APL, we give special names to certain shapes of data:
Term |
Meaning |
---|---|
Scalar |
is used for a single value, a number like |
Vector |
is a plain list of values. It may be composed of numbers like |
Array |
is a generic word for any set of values, whatever the number of its dimensions (whatever its shape). |
Matrix |
is an array with 2 dimensions, like |
Table |
is another common word used for arrays with 2 dimensions (matrices). |
Cube |
is a common word used for arrays with 3 dimensions. |
1.6. Reduction Unifies Traditional Notations#
Perhaps you remember the variable costs
:
costs
So what must we do to work out the total? Mathematicians are creative people who long ago devised the symbol \(\sum\), always with a pretty collection of indices above and below, which make it complex to understand and to type on a typewriter:
In APL, the operation is written like this:
+/ costs
Simple, isn’t it? This gives the total of all the items of the array. You can read this as “plus reduction” of the variable costs
.
To gain a better understanding of the process: when we write an instruction such as
+/ 21 45 18 27 11
it works as if we had written
21 + 45 + 18 + 27 + 11
and we obtain the sum 122
.
In fact, it works as if we had “inserted” the symbol +
between the values. But then, if we write
×/ 21 45 18 27 11
it works as if we had written
21 × 45 × 18 × 27 × 11
so, we get the product 5051970
.
Similarly, if we write
⌈/ 21 45 18 27 11
it works as if we had written
21 ⌈ 45 ⌈ 18 ⌈ 27 ⌈ 11
so, we obtain the largest term 45
Reduction, represented by the symbol /
, belongs to a special category of symbols called operators. All the other symbols (+
-
×
⌈
⍴
…) are called functions (addition, subtraction, multiplication, maximum, shape, etc.).
The arguments of a function are always data (arrays):
price × qty
Whereas the “arguments” of an operator (which are called operands) can also be functions:
+/ qty
The left operand of reduction can be one of many of the APL symbols, and it can also be the name of a user-defined function. This may give you an idea of the generality and power of the concept.
Dyalog APL contains around 20 such powerful operators. If that is not enough, you can even write your own operators, just like you can write your own functions!
1.7. Let’s Write Our First Functions#
Imagine that we want to calculate the average of the following numbers:
val ← 22 37 41 19 54 11 34
We must first calculate the sum of the values:
+/ val
Next, calculate the number of values:
⍴ val
And finally divide one result by the other.
The calculation can be written as the single formula:
(+/val) ÷ (⍴val)
As it is quite likely that we shall often want to make this sort of calculation, it is preferable to store this expression in the form of a user-defined function. User-defined functions may be used in the same way as the built-in functions represented by special symbols like +
-
×
÷
>
⍴
…, which are called primitive functions, the major distinction is that the former are introduced by the user, whereas the latter already come with APL.
To define a simple function like this one, here is the easiest way:
Average ← {(+/⍵)÷(⍴⍵)}
Average
is the function name;⍵
is a generic symbol which represents the array passed on the right (type it with APL+w);⍺
would be the generic symbol for the array passed on the left, if any (type it with APL+a).
The definition of the function is delimited by a set of curly braces {
and }
. For more complex functions it is also possible to use a text editor, but this is beyond the scope of this short introduction.
Once defined, this function may be invoked in a very simple way:
Average val
During the execution above, ⍵
gets the values in val
.
Average 12 74 56 23
Let us also write two little dyadic functions, the left argument of which is ⍺
, and the right is ⍵
:
Plus ← {⍺+⍵}
Times ← {⍺×⍵}
(3 Plus 6) Times (5 Plus 2)
As you can see, these functions behave exactly as if we had written (3+6) × (5+2)
.
We said in the preceding section that a user-defined function could be used by the reduce operator; let us try:
Plus/ val
It works!
1.8. Indexing#
Returning to our vector of numbers val
:
val
In order to extract the 4th item, we just write:
val[4]
This is similar to most of the most popular programming languages. What is new is that one can extract several items in one instruction:
val[2 3 4]
Or that you can have repeated indices to extract the same item twice or more:
val[2 4 7 1 4]
And of course, in the same way, one may modify one or more items of val
using their indices. Naturally, one must provide as many values as there are items to modify, or a single value for all:
val[3 5 1] ← 0
val
val[3 5 1] ← 300 77 111
val
You can see that the third item is now 300, the fifth item is now 77 and the first one is 111.
It is often necessary to extract the first few items from a list of values, for example the first 5. Nothing could be easier:
val[1 2 3 4 5]
But if one needs to extract the first 500 items from a long list, typing the integers from 1 to 500 would of course be very inconvenient.
This is why APL has been given the symbol ⍳
(iota), which produces the set of the first \(n\) integers (⍳
can be obtained using APL+i).
Thus, instead of writing 1 2 3 4 5 6 7 8
, it is sufficient to write ⍳8
.
And to extract the first 500 terms of a large vector, one may write: big[⍳500]
.
We shall discover later an even simpler method.
1.9. Calculating Without Writing Functions#
The employees of a company are divided into three hierarchical categories, denoted simply 1, 2, and 3. One assigns to two variables the salaries and the categories of these employees, as shown here:
⎕RL ← 73
salaries ← ?20⍴5000
salaries
⎕RL ← 73
categories ← ?20⍴3
categories
Do they never want to increase these salaries? (What has our poor world come to!)
A rumour reaches us about their plans: they want a different percentage increase for each category, according to the following scale:
Category |
Suggested increase |
---|---|
1 |
8% |
2 |
5% |
3 |
2% |
How much is this going to cost the company?
We create a variable containing the above three rates. Notice how APL allows us to divide three numbers by a single one:
rates ← 8 5 2 ÷ 100
rates
The first employee is in category 1, so the rate that applies to them is:
rates[1]
It follows that the first five employees, being in categories 1 2 2 2 3
respectively, are entitled to the following increases:
rates[1 2 2 2 3]
More generally, the rates applied to all of our employees could be obtained like this:
rates[categories]
Having the rates, one just has to multiply by the salaries to obtain the individual increases:
salaries × rates[categories]
Finally, by adding them all, one will know how much it will cost the company:
+/ salaries × rates[categories]
You may note that:
the expression remains valid whatever the number of employees or categories;
the result has been obtained without writing any function or complex program; and
this expression can be read as the simplest possible English, like this:
Sum the
salaries
multiplied byrates
according tocategories
.
Clever, no?
This illustrates how the expression of a solution in APL can be very close to the way that the solution could be phrased in everyday language. This also shows clearly that the ways of reasoning induced by traditional programming languages are not the only possible ones. This difference and originality, introduced by APL, are among the major features of the language.
1.10. Friendly Binary Data#
APL makes much use of binary data. It is most often created by means of relational functions like =
or >
, which give the answer 1
or 0
, depending on whether the relation is true or not:
salaries > 3000
By the use of the correct relational function, one can immediately compare the actual
results with the forecast
and find the favourable results:
actual > forecast
APL offers the conventional mathematical form of the six relational functions:
< ≤ = ≥ > ≠
Naturally, one can operate on this binary data using all the functions of Boolean algebra, and moreover, the symbols used are those familiar to mathematicians of all nationalities around the world:
the function and is represented by the symbol
∧
(represented by the word “and” or the symbol&&
in many programming languages); andthe function or is represented by the symbol
∨
(represented by the word “or” or the symbol||
in the same languages).
Thus, if I am looking for people in category 3 whose salary is less than 4000 euros, I can write:
(categories = 3) ∧ (salaries < 4000)
In fact, APL offers all the functions of Boolean algebra, including some perhaps less familiar functions like nor and nand (not-or and not-and), but they are very useful in finance and electronic automation.
There is, however, no special symbol for the function exclusive or (often called xor). This is because it is not needed: the function not equal ≠
gives the same result as exclusive or when it is used with Boolean values, as you can see below:
0 0 1 1 ≠ 0 1 0 1
Finally, not only can these binary vectors be used as we have described but also for novel purposes, such as counting and selecting.
1.10.1. Counting#
Having found which salaries are less than 2500 euros by means of the following expression:
salaries < 2500
It is easy to add all the 1’s and 0’s to calculate how many people earn less than 2500 euros:
+/ salaries < 2500
1.10.2. Selection#
One can also use the binary vector as a “mask” to select the items corresponding to the binary 1’s from another array:
1 1 0 1 0 0 1 / 23 55 17 46 81 82 83
The procedure is identical for character data:
1 0 1 0 0 0 0 1 1 / 'Drumstick'
This function, called compress, is particularly useful for extracting items conforming to a given criterion from a variable. For example, to display the salaries of people in category 2, one writes:
(categories = 2) / salaries
Looks like a powerful primitive function, doesn’t it?
1.10.3. Discovery#
To practice our skills some more, let us find in our variable val
the positions of numbers greater than 35. Here are the necessary steps:
val ← 22 37 41 19 54 11 34
val
val>35
⍴val
With this we can generate all possible positions of items in val
:
⍳⍴val
Let us compare two of these results:
val>35
⍳⍴val
You can see that if you eliminate (using compress) the items which correspond to zeros in order to retain only those corresponding to 1, you easily get the positions required: 2 3 5
.
Thus the job may be done as follows:
(val>35) / ⍳⍴val
This expression is applicable in many different situations.
Here is a similar use, but applied to character data to find the positions of “a” within a phrase; the method is the same:
phrase ← 'Panama is a canal between Atlantic and Pacific'
(phrase = 'a') / ⍳⍴phrase
If you want, you can check the result by hand… but I wouldn’t bother!
1.11. A Touch of Modern Math#
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 = 'aeiouy'
⁽¹⁾, because that would require the item by item comparison of a phrase of 46 letters and “aeiouy” which has only 6.
In other words: You may compare 46 letters with 46 other letters, or compare them with one letter, but not with 6.
So we shall use a new function: membership, which is represented by the symbol ∊
, also used in mathematics (∊
can be obtained by pressing APL+e).
The expression X ∊ Y
returns a Boolean result which indicates which items of the variable X
appear in the variable Y
, wherever they may be. And it works no matter what are the shapes, the dimensions or the type (numeric or character) of X
and Y
, a pure marvel!
⁽¹⁾ “Y” is considered to be a vowel in many European languages.
For example:
5 7 2 8 4 9 ∊ 3 4 5 6
Only the 5 and the 4 were found in 3 4 5 6
.
'dandelion' ∊ 'garden'
The letters “lio” are not in the word “garden”.
So in pursuit of our inquiry we shall write:
(phrase ∊ 'aeiouy') / ⍳⍴phrase
One can also use membership between a vector and a matrix, as shown below. We have represented side by side the variable itself and the result of using membership:
towns ← 6 10⍴'Canberra Paris WashingtonMoscow Martigues Mexico '
towns, towns ∊ 'aeiouy'
We can reverse the expression, but the result has always the same shape as the left argument:
'aeiouy' ∊ towns
The trailing 0 is because none of the towns has a “y” in it.
1.12. A Powerful Search Function#
We have harnessed a very useful method to look for the positions of letters or numbers in a vector, but the answer obtained does not provide a one to one correspondence between the search values and the resultant positions.
Here’s a vector of values:
list ← 15 40 63 18 27 40 33 29 40 88
And these are the numbers we want to find:
where ← 29 63 40 33 50
Now we apply our method and these are the positions found:
(list ∊ where) / ⍳⍴list
The positions are correct, but 29 is not in position 2, and 40 is not in position 6.
The question we have answered using the expression above is: “in which positions in list
do we find a number that also appears somewhere in where
?”.
If we want to answer the slightly different question “where in list
do we find each number in where
?”, we need to use a different method.
This new method uses the dyadic form of the symbol ⍳
(iota):
list ⍳ where
It is true that 29, 63, 40 and 33 occur respectively in positions 8, 3, 2 and 7. It’s much better!
But, first surprise: the value 40 occurs 3 times in list
, but only the first one is reported in the result. This is because, by definition, dyadic iota returns only the first occurrence of a given item. If the response for each value sought has to match a position, how may one, looking for 5 numbers, obtain 7 results?
Second surprise: the value 50 is reported as being found in position 11 in a vector comprising only 10 items! This is how the function index of (dyadic ⍳
) reports that a value is absent.
At first sight this seems a bit surprising, but in fact it is a property which makes this function so generally powerful, as we shall soon see.
1.12.1. An Example#
A car manufacturer decides that they will offer their customers a discount on the catalogue price (you can see how this example is imaginary!).
The country has been divided into 100 areas, and the discount rate will depend on the area according to the following table:
Area |
Discount |
---|---|
17 |
9% |
50 |
8% |
59 |
6% |
84 |
5% |
89 |
4% |
Others |
2% |
The problem is to calculate the discount rate that may be claimed for a potential customer who lives in a given area d
; for example
d ← 84
Let us begin by creating two variables:
area ← 17 50 59 84 89
discount ← 9 8 6 5 4 2
Let us see if d
is in the list of favoured areas:
d ∊ area
We can see d
is a favoured area, and we can even see its position in the list of favoured areas:
area ⍳ d
Let us find the current rate of discount for this index position:
discount[4]
So this customer can get 5% discount.
One may simply write
discount[area⍳d]
If a customer lives in any area such as 75, 45, or 93, the expression area⍳d
will in all cases give the result 6, because those values are absent in area
. Then discount[6]
will always find the rate 2%, as expected.
The importance of this approach is that it is vector-based. Suppose that publicity attracts crowds and that therefore d
is no longer a single value but a vector. The solution is still valid:
d ← 24 75 89 60 92 50 51 50 84 66 17 89
discount[area⍳d]
All that without a function, neither “loop” nor “test”, and whatever the number of areas. Readers who know other programming languages will have no difficulty in making the comparison.
1.12.2. Generalisation#
In truth, the expression we just wrote is an example of an algorithm for “changing the frame of reference”. Don’t panic, the name may seem esoteric, but the concept is simple. A list of area numbers (the initial set) is translated into a list of discount rates (the final set).
Let us now imagine the initial set to be an alphabet composed of lower case and upper case letters, and the final set to be composed of only upper case letters (with a blank space in the middle):
alphLower ← 'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ'
alphUpper ← 'ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ*'
Notice that alphUpper
is one character longer than alphLower
. We have added an asterisk at the end, and you will see why we did so.
Here is a little French sentence, with one accented letter:
tale ← 'Le Petit Chaperon-Rouge a bouffé le Loup'
The expression below converts from lower to upper case.
alphUpper[alphLower⍳tale]
As one might expect, the characters -
and é
, which are absent from the initial alphabetic set, have been replaced by the *
of the final set, but the conversion is acceptable. This solution can easily be improved.
Once more, the rational steps to be taken to create a solution are easily translated into a programming algorithm, and the programmer can thereby get a much more extensive insight into the problem itself.
1.13. After Values, Let Us Process Shapes#
Many traditional programming languages do not really handle arrays of numbers or characters. They hold them in memory, but when the arrays are required for processing they can only be handled one item at a time. It is not surprising, in these circumstances, that these languages have only limited means of controlling the shape of the data.
It is quite the opposite in APL, which offers many tools for working with the shape of the data. We shall only look at a few of them here.
1.13.1. Take and Drop#
The functions take (↑
, typed with APL+y) and drop (↓
, typed with APL+u) serve, as their names suggest, to extract part of a set of values. Here we shall show only examples based on vectors, but all the other shapes of data can be treated in a similar way.
Recall that
list
We can take its first 4 elements:
4 ↑ list
Or we may drop the first 5 elements:
5 ↓ list
If the left argument is negative, these same functions count from the end of the vector.
For example, we may take the last 3 items of the vector:
¯3 ↑ list
Or we may drop the last 7 items, leaving the first 3 ones:
¯7 ↓ list
The last result is the same as obtained by
3 ↑ list
Earlier, we used big[⍳500]
to extract the first 500 items of big
. We can now see that we also could have used 500↑big
.
Here again, using these new symbols, it is possible to create innovative solutions to classical problems.
Let us imagine a business with a turnover which has grown over 12 years.
The variable tome
is the turnover in millions of euros.
tome ← 56 59 67 64 60 61 68 73 78 75 81 84
tome
We want to calculate the difference between each year and the year before; how can we do it?
If we drop the first value, we get
1 ↓ tome
and if we drop the last value, we get
¯1 ↓ tome
In each position of the first result we have “this year’s turnover” and in the same position in the second result we have “the previous year’s turnover”.
We see that all that remains is to subtract these results item by item. We do this without a function or loops; all very simple!
(1↓tome) - (¯1↓tome)
In place of a subtraction, a division would calculate (with some obvious adjustments) the rates of growth instead of the differences:
100 × ( (1↓tome) ÷ (¯1↓tome) )-1
Let us put that in a small user-defined function, and apply it:
Growth ← {100×((1↓⍵)÷(¯1↓⍵))-1}
Growth tome
1.13.2. Reverses and Transposition#
APL is also well equipped with functions to pivot data about any axis, as suggested by the appearances of the symbols used. They apply to both numeric and character data, as we are going to show by applying these functions to the variable towns
that we used earlier.
The symbols used hereafter are obtained like this:
⌽
with APL+Shift+5;⍉
with APL+Shift+6;⊖
with APL+Shift+7.
Recall the value of the initial variable:
towns
We can reverse around a vertical axis with ⌽
(notice the vertical bar in the glyph):
⌽towns
We can reverse around a horizontal axis with ⊖
(notice the vertical bar in the glyph):
⊖towns
And we can swap rows and columns (what we call tranpose) with ⍉
:
⍉towns
And all four of them side by side for comparison:
(towns) (⌽towns) (⊖towns) (⍉towns)
The symbols used (⌽
⊖
⍉
) are self-explanatory, no effort is required to remember any of them. They also have dyadic uses, but we shall not demonstrate them here.
1.14. Back to Primary School#
Remember when we learned our multiplication tables? In that practically Paleolithic era, to make sure we knew all our tables, my teacher made us calculate the multiplication table for the integers 1 to 9:
You see, I haven’t forgotten!
Probably you have done all this just like me. And then we quickly forgot that very powerful tool, one which APL provides under the name outer product.
The task consists of taking all possible pairs of items of two vectors, (the column and row headings) and making them the left and right arguments of the function at the top left. For example, 3 times 7 gives 21 (in red here above).
Next, we shall go on to see what we get if we change the values a little:
This operation is written as follows in APL:
5 4 10 3 ∘.× 8 5 15 9 11 40
The outer product symbol is made of a small circle (APL+j), a dot, and the function to be applied. It is an operator, as it takes a function (×
in this case) as an operand.
Despite of its name “outer product”, this operator by no means is restricted to working with multiplication. We can replace the symbol for multiplication by any other dyadic function (like =
<
≥
or ⌈
), or even functions which you have defined yourself (like Plus
), and you will understand, as for reduce which we saw earlier, that outer product is an operator of amazing power.
Let’s have some fun with it:
((⍳5)∘.=(⍳5)) ((⍳5)∘.<(⍳5)) ((⍳5)∘.≥(⍳5)) ((⍳5)∘.⌈(⍳5)) ((⍳5)∘.Plus(⍳5))
1.14.1. A Useful Application#
Suppose the vector ages
contains the ages of 400 respondents to an opinion poll. We want to establish how many people there are in each of the following categories: 0 - 25 - 30 - 35 - 45 - 50 - 55 - 65 or above.
Here is the data:
⎕RL ← 73
ages ← ?400⍴100
ages
category ← 0 25 30 35 45 50 55 65
We are going to use the outer product category ∘.< ages
, and here are the first items of the result:
If one adds up this Boolean matrix, one obtains for each row the number of people who are older than 0 years, older than 25 years, older than 30 years, etc. This is the expression:
counts ← +/ (category ∘.< ages)
counts
With the cut-down extract shown above, the value of counts
would be 7 4 4 4 3 2
.
In other words, there are 7 people older than 0 (all of them). Among them, 4 are older than 25. In order to know how many people are between 0 and 25, it is necessary to calculate 7 - 4
to obtain 3
.
If one wants to reproduce this calculation for all categories, it is necessary to perform a series of subtractions as here:
where the first line is counts
, the second line is counts
without its first item and followed by zero and the last line is the result as obtained by the expression counts-(1↓counts,0)
.
To append a zero to the right, we used a comma, which joins variables together. This is a function called catenate.
If one no longer works with a small extract of data, but with the full list of 400 people, this is what one gets:
counts ← +/ (category ∘.< ages)
counts - (1↓counts,0)
All that with little programming, and it works whatever the number of people or categories. What a luck!
Once again, APL allowed us to find straightforward and original solutions to traditional problems.
1.15. There Is a Lot to Discover Yet#
In the course of these pages we have flown over APL country and glimpsed certain bold ideas which explain the attraction of the language. A thousand other things remain to be seen! If you are convinced that Dyalog APL is worth the effort, you can start studying APL in much more detail in the rest of this book.
Let us just discover some additional attractive features of APL.
1.15.1. Attractively Simple Syntax Rules#
Most other programming languages contain rather complex rules to determine how an expression is evaluated, a concept called operator precedence. Very often it says that, for example, multiplication and division have higher precedence than addition and subtraction, meaning that an expression like \(5 \times 3 + 2\) gives \(17\), because the multiplication is done first, and then the addition.
This sounds simple and familiar, but it quickly gets very complex and difficult to remember, especially in a language containing many functions, like APL. For example, which precedence should we give to ⍳
or ↓
or ⌈
, for which we do not have an established tradition? And what about the functions we write ourselves?
The democratic solution adopted in APL is “We hold these truths to be self-evident that all functions are created equal!”. The only and very simple rule is that any function works on the result of the entire expression to its right, and, if it is a dyadic function, the value immediately to the left of it. As usual, parentheses can be used to group parts of an expression.
So, let us see how this applies to the expression above:
5 × 3 + 2
×
works on 5
(the value immediately to the left of it) and the result of 3 + 2
, the entire expression to the right of it. Even though it is not strictly correct, many people say that APL evaluates from right to left. In any case, the result of the expression is 25
in APL!
Had we written (5 × 3) + 2
instead, the result would of course have been 17
.
It may take a little while to get used to this slightly unfamiliar rule, but once it has been learned it is really a great advantage because you can direct your energy towards solving your problem and not have to remember complex rules just to satisfy the computer’s need for guidance.
1.15.2. Use Many Other Calculating Tools#
We have discovered some original functions, which are completely absent from most other programming languages, like ⍴
, ↑
, ⍉
, ∊
and outer product. Those features lead to new methods and new algorithms to process data; this is one of the main advantages of APL.
Not only do you have a lot more functions, like inner product (generalised matrix product) and many built-in mathematical tools (trigonometry, matrix inverse, conversions to and from any numerical base, etc.), but you can also handle generalised (nested) arrays: arrays which contain arrays, which themselves contain arrays, and so on.
The scope of the possible solutions to a single problem is often so wide that it is probably the reason why people never get tired of using APL: they always have something new to discover and to invent.
1.15.3. Create User-friendly Applications with the GUI#
Like all modern programming languages, Dyalog APL has a Graphic User Interface (GUI) under Microsoft Windows and Win32 emulators under Unix. It allows you to design pleasant user interfaces with all the items and features you are familiar with. Even a beginner can quickly create an interface and process the data with all the power of the language, whereas in traditional languages, the same operation would need days or weeks of programming. The Microsoft.NET interface supports the use of WinForms and Windows Presentation Foundation GUI elements as well.
1.15.4. Access Your Data#
Of course, Dyalog APL has facilities to access data files, and SQL databases like Oracle, SQL Server, or other popular databases. These interfaces allow you to visualise and process part or all of a database as if it were an array, to which you can apply all the array processing functions available in APL. That saves you a lot of heavy programming tasks.
Dyalog APL has also its own powerful file system. These files are collections of arrays of any shapes or sizes, which can be processed with the full power of the language. Databases built with this special feature are extremely compact (2 to 3 times smaller than an equivalent relational database). They can be shared, and they offer much greater flexibility and superior performance compared to traditional database management systems.
1.15.5. Build an Efficient Partnership With Microsoft Excel#
Microsoft Excel, probably the most popular spreadsheet manager, is used all over the world by millions of people. It appears that Excel is an easy way to enter data into a computer in a tabular form, and it is also an excellent product to produce everyday business reports and graphs.
While Excel is convenient for small business applications, it is too limited to process complex calculations, or when some operations involve data located in many workbooks.
Excel and APL can easily be combined in an efficient partnership. Excel is used to input data in a very flexible way into spreadsheets, which most users are familiar with. APL can then read dozens (and sometimes hundreds) of sheets, aggregate the data, and perform very complex calculations, which may be controlled and parameterised through the GUI interface. Of course, the results can be printed, but they can also be output to the users in the form of specially prepared Excel worksheets. In these worksheets, the users can proceed to additional operations for their particular needs, or produce graphs of their own.
1.15.6. And Also…#
You can interface APL with the Internet, write your own web server, use multithreading to process simultaneous tasks, use all the advantages of true object oriented programming, and use many attractive features, which are beyond the scope of this quick survey.
1.16. FAQ#
Perhaps you have found this language rather engaging, but before you decide to invest time and energy in developing APL applications, you would like to be sure of your choice. Let’s give you some answers.
1.16.1. Is Dyalog APL A Professional Tool?#
Among many others, here are some significant examples of important applications:
long-term board-level financial planning for one of the world’s five biggest petroleum companies, used over 12 years;
the management of supplies required from ‘today + 2 days’ to ‘today + 3 months’, by the assembly lines of the 6 principal factories of a major international car manufacturer; and
risk management for an important insurance group.
These three examples have common characteristics, positioning them as major industrial applications:
they are particularly crucial because considerable finances are at stake;
they must be absolutely reliable. A major car manufacturer works must not be brought to a stop by a programming bug; and
the first two applications operate in a highly volatile business environment. As their requirements are always changing, the programs undergo constant mutation. This must be made with very short development cycles.
So we can answer: yes, for a reasonable cost in labour, APL makes it possible to create, maintain, support, and further develop large, sensitive applications of the highest level of quality, reliability, and flexibility.
1.16.2. Can Dyalog APL Fit a Professional Developer’s Needs?#
The characteristics of APL make it easy to use, lead to quick development, and help to produce light and flexible code. Programs developed in APL can evolve quickly, to fit changing user requirements at will. Here are some characteristics of APL to take into account:
because APL uses symbols rather than words to represent operations, a programmer can use any word for his own data and functions’ names; they will never be in conflict with the language structure and contents;
due to its array-processing capability, APL dramatically reduces the needs for programming loops. Because all such intricacies are removed, the code is much lighter, and the programmer can concentrate all his attention and skill on the true core of computing requirements;
in many programming languages, a programmer would have to declare that each variable will be an array of such and such dimensions, containing values with a specific data type. There is nothing similar in APL. The size and the data type of a variable arise from the way in which the variable has been produced. If one extracts 2 rows and 5 columns from a matrix, the result will of course be a 2 by 5 matrix. And if we divide 486 into 7, the result will of course be a fractional value, there is no need to specify it in advance;
the extensive set of direct operations on data offered by APL leads to new approaches. For traditional problems, which have been solved in the same way in most programming languages for years, APL suddenly offers new solutions, which appear to be light, straightforward, very general, and easy to maintain because they are easy to read; and
the readability of APL often surprises people who practise other programming languages; they probably forget that the languages they use are totally obscure for most non-specialists. APL is learned and used efficiently by people who are not data processing professionals, but are instead specialists in their professional fields, such as accountancy, chemistry, insurance, logistics, finance, and biology. They have less difficulty with APL than with the problems they have to solve, and most often, they solve them with APL. They could not achieve this alone with any other programming languages, but would require the help of programming specialists… who generally know nothing of the problem domain in question.
1.16.3. Where is APL Typically Used?#
APL is typically used in situations where there is a lot to be gained if people who understand a problem can be closely involved in developing solutions. Sometimes application code is written in the afternoon by the same people who read the latest research reports or legislation in the morning - or by members of a very small team who have overlapping skills.
In a more traditional approach, in which the specialist teams would need the help of professional programmers, such teams may find that valuable information was lost in the “process”, so that several iterations would be required in order to reach a satisfactory solution. Especially when a problem has a mathematical or technical foundation, APL can turbo-charge the development cycle.
Even if there is no immediate urgency, APL allows the path between “users and coders” to be much shorter than is the case when “traditional” technologies, in which requirement gathering, specification, architecture and coding are often handled by separate teams. If you have a novel idea that you would like to investigate (or “get to market”) quickly, you may reach your goal very much more easily by learning APL or employing a small team of APL developers (who will be able to “speak your language”), rather than becoming or using “programmers” to develop solutions for you.
APL is most widely used in the financial industry, which has a mathematical foundation, and rapidly changing requirements: in this environment, the use of APL can provide a significant competitive advantage.
Unfortunately, some problems cannot wait!
Great flexibility and speed is the true commercial foundation for APL. With APL one can develop in direct contact with the users and involve them from the outset in the continual modification of the object of the development. Afterwards, as an application continues to evolve, it is still the speed of development which makes APL a tool especially well adapted to changing environments.
If you still think APL might be the very tool you need, you are ready to read the full text of the following tutorial. You will revisit some of the examples you have seen above, plus many, many others.