Classic Computer Magazine Archive CREATIVE COMPUTING VOL. 9, NO. 11 / NOVEMBER 1983 / PAGE 228

APL; a language of pleasant surprises. Vincent Stanford.

APL, A Programming Language, is both very different from Basic and very powerful. After almost ten years of programming in Fortran, Basic, assembly language, ad many structured varianst of these languages, I decided to try it when it became available for my TRS-80.

I have been consistently amazed at the power of APL. APL is different, fundamentally different from the more commonly used languages. One of the main differences is that APL handles arrays as objects as well as individual entries. For example, consider how an array is moved to another array in Basic for two arrays X and Y (of two dimensions each): 10 for i=L1 to H1 20 for j=L2 to H2 30 Y(ij)=X(ij) 40 next j 50 next i The corresponding construct in APL is given as: Y left arrow X

The reason for this difference is that Basic is what John Backus, the author of both Fortan and Algol (yes, the same man was responsible for both) calls a Von Neuman Language.

The Von Neuman computer, which is the prototype for most modern computers, consists of memory for storage and an accumulator and index registers for processing. To move a block of storage on such a machine, each memory location in the source block must be stored in the appropriate location of the destination block. To add two sets of numbers in two blocks of storage as in vector addition, an element from the first block must be loaded into the accumulator, the load instruction being indexed with the contents of an index register if a loop is being used, and an element from the second block must be added to the accumulator and the resultant sum storer at the desired location. In other words, almost all processing activities must be performed one number at a time and pass through the accumulator.

Higher level languages such as Fortran and its direct descendants, Algol, Basic, PL/1, and Pascal reflect this basic underlying computer architecture. In these languages, just as in assembly language, an algorithm must, ultimately, present a stream of numbers to an accumulator for processing: all the numbers must line up single file to pass through a bottleneck.

For example, consider the following program segments, both of which compute the total of the numbers contained in an array X: Basic APL 10 T=0 20 for i=1 to n T left arrow +/X 30 T=T+X(i) 40 next i

In the Basic algorithm, we are required to deal with each entry of X as an individual number using a "for" loop and an intermediate total for each step. In APL we can use two powerful array operators left arrow and /, and can treat that array X as an entity. In APL we "reduced X under addition," the reduction of an array X under an operator circled positive being defined as: (. . .(X1 circled positive X2) circled positive X3) circled positive . . .) circled positive Xn In the case of addition for circled positive this is simply: X1 + X2 + . . . + Xn

This approach to programming allows the subordination of nonessential detail; it cuts down on unnecessary clutter. This can be seen by considering, in order, each discrete step required to implement the two algorithms. Basic

1.) The variable used for the total must be given a name and initialized if it has been used before.

2.) A loop must be set up.

3.) An index must be chosen.

4.) A lower bound for the index must be chosen, in this case, 1.

5.) An upper bound, n, for the index must be chosen.

6.) A formula to compute the partial sums which lead to the desired grand total must be devised. This involves:

a.) Two references to the variables containing the total.

b.) A reference to the array X.

c.) A reference to the array index i.

d.) A special knowledge of Basic syntax to differentiate the occasions in which = means = (equals) and when it means left arrow (gets). Also, in high school algebra, T=T+x(i) implies that 0=X(i).

7.) The loop index must be incremented by the programmer using the "next" statement.

Together these discrete steps constitute roughly ten distinct possibilities for error. An important related point is that humans have, on average, the ability to hold seven plus or minus two items in short term memory. That means that even a small segment of code can exceed our ability to perceive directly the function being performed.

By contrast, consider the steps below: APL

1.) The variable to be summed must be named.

2.) The reduction operator must be invoked.

3.) The + must be used to indicate reduction under addition.

In APL, only three discrete items must be kept in mind to perform the summation. No loop and no index are needed.

Another, less trivial programming example shows that the difference in labor can be even greater than that described above. Programs which sort an array called X are shown in Basic and APL in Figure 1. A bubble sort is used in the Basic example.

Space and patience prohibit a careful analysis of all the elements required to do the sort in Basic. In APL, by contrast, we know only that upward arrow is an array operator which produces a "sort vector" which is then used as a kind of a generalized subscript to unscramble X. Yes, in APL arrays may be used as subscripts. For example: X [3 4 5] is equal to 22 67 9 if X is the array sorted in the example above.

So APL is based on operators which work on arrays rather than on numbers. Another thing that constantly contributes to the endless string of pleasant surprises about APL is that these operators usually have more than one meaning. This combines with the completely dynamic nature of APL variables to form the basic for a truly delightful computer language.

For example, suppose that the following statement were executed (X need not be declared in advance): X left arrow 100 99 98 97 96 95 94 93 9291 Then X [5 6 7] takes the values 96 95 94, which are just the middle five through seven numbers of the X array. Next, suppose that the following statement was executed: X left arrow 'The Middle Characters' X has now changed to a character variable from a numeric one, and from 10 to 21 elements. But the expression X [5 6 7] still has a valid meaning: X [5 6 7] has the value Mid So, the index operator [ ] serves not only the role of the Basic subscript but also that of the substring operator MID$. Everything seems to work that way, which makes it an enjoyable language to play with. Uses Of APL

APL is especially strong in the area of statistical computation. This makes it an excellent aid for statistics course work and hard science laboratory work. Many problems which require either long sequences of hard calculations or cumbersome Basic programs can be accomplished with a few symbols in APL. For example, a function to compute the mean of a vector X which is defined mathematically as: can be defined in APL80 as follows: )def Mean. of. data left arrow mean data 1: Mean. of. data left arrow (+/data)-P data Several features of this function will be new to Basic programmers. The long and self-explanatory name Mean.of.data is one. APL80 names may be up to 2 characters in length. The shape operator P (often called either rho hor shape) tells how many elements are in data at the current time. This can change at the option of the programmer in APL. Similarly the variance of a vector X is defined mathematically as: Where X is the mean of X. An APL80 function to compute the variance could be as follows: )def Variance.of.X left arrow Var X 1: Variance.of.X left arrow (+/(X-mean X)*2) divided by ((pX)-1) Note the structural similarity of statement 1 of Var to the original formula. No loops were needed. Often in APL programs operators such as reduce (/), which handle arrays, take the place of loops. Note also that we were able to make immediate use of the mean function defined earlier.

The Pearson correlation coefficient computed from two vectors X and Y is defined mathematically as: This can be implemented in APL80 as: )def R left arrow X corr Y 1: X left arrow X-mean X 2: Y left arrow Y-mean Y 3: R left arrow (+/X*2)X(+/Y*2) 4: R left arrow (+/X*Y) divided by R*5 Again, this is simply implemented in APL with only a few symbols and array operators.

A function which produces a histogram of a set of data points can be simply defined as follows in APL80: )def plot left arrow X hist grid; cell; freqs; max. freq 1: cell left arrow +/X.sup.0 > 2: freqs left arrow +/ipgrid)sup. 0=cell 3: max. freq left arrow GAMMA/freqs 4: plot left arrow '*' [1+(i max.freq)sup. 0.= is greater than freqs))] This routine is complicated enough so that discussion of how to read APL is in order. APL is best read with the aid of the APL interpreter. Especially at first, the language symbols look like hieroglyphs and are not easy to comprehend. A good way to learn is to make up test values for the variables used and to execute the statements pieice by piece and examine the intermediate results in the immediate mode of the interpreter to explain and verify what has been written in the function.

First, though, we should take a look at the operators used. Remembering that APL executes statements from right t left, the first operator we encounter in the function is the [deg.] . . . This is called the outer product, sometimes pronounced "jot dot." It requires two variables and diadic operator as its operands. Roughly speaking, it forms all possible pairs products of the elements of the two variables under the specified operator. This is simpler than it sounds. Let's make up some data for the histogram variables: grid left arrow 0 100 200 300 400 500 and X left arrow ?10p600 X 545 297 364 205 140 398 321 80 438 27 The ? command will give us ten random numbers between 1 and 600. The expression X. sup.0 > grid is now defined as shown in Table 1. Notice that 545, the first entry, is greater than 500, so the row in the outer product corresponding to it contains five ones. This means that it is in the "greater than 500" class in the histogram. The next one down, 297, corresponds to a row with three ones or the "greater than 200 but less than or equal to 300" category.

If there were only some way to add up the ones in each row, we would have the histogram class for each of the numbers in X. Plus reduction (+/) added up the numbers in one row, which was a vector. In APL most operators generalize to two and higher dimensions of arrays. A natural, and in APL usually successful, impulse would be to try the expression +/X.sup.0> grid and see what results. cell left arrow +/X.sup.0 > grid cell 6 3 4 3 2 4 4 1 5 1

A quick check of Table 1 shows that plus reduction summed up the two dimensional array X.sup.0 > grid in a row major order which results in the class of each data point. This array of classes is assigned to the variable name "cell" in the histogram algorithm.

The next line uses another jot dot, this time a jot dot of =. But, what is the meaning of ipgrid? Ask your APL interpreter: pgrid 6 ipgrid 1 2 3 4 5 6 i(iota or "index") of N is a vector containing the integers 1 though N. As before (ipgrid)sup.0=cell is defined as shown in Table 2, and APL tells us: square left arrow freqs left arrow +/(ipgrid)sup.0=cell which are exactly the frequency counts for each class given by grid. This vector is assigned to freq in the function for subsequent use.

The next line shows another generalization of an operator. Plus reduction has just been used to good effect; now max reduction is also used. The operator GAMMA chooses the larger element of a pair. GAMMA/array chooses the maximum element of an array.

Line four of the histogram function actually creates the plot of the frequency counts. As above, the way it accomplishes this can be understood by starting at the righthand side of the expression and breaking it down into small pieces. This proceeds as follows: Max.freq 6 iMax.freq 1 2 3 4 5 6 freqs.sup.0 is not > Max.freq

Table 3 shows the result of this operation. Since zero is not a valid subscript in APL80, 1 is added to (freqs sup.0 is not > Max.freq) to transform the zeros and ones to one and twos respectively. These are used as subscripts to the character array '*' to convert the ones and twos to blanks and asterisiks. The expression in the final line of the function results in: '*' [1+((iMax.freq)sup.0 is not > freqs)]

Many other basic statistical computations can be done with very small APL80 functions which are quickly and easily written. I have found the orderliness and generality of APL a continuing source of surprises. Always pleasant surprises--APL.