Revisiting Rough Spots

Kenneth E. Iverson
 

Adepts in any discipline may benefit from a re-examination of the more difficult parts of their own development in light of later experience, and may perhaps use the insights gained thereby to enlighten the presentation of topics to others. For example:

 

In APL the treatment of the outer product was deferred to Chapter 40 of an early manual because of its perceived difficulty; such treatment was eventually rectified by the remark of a high-school teacher (Linda Alvord) who said that these were simply the addition and multiplication tables of elementary arithmetic rendered alarming by the adoption of a term from tensor analysis.
 

 

The terms vector and matrix adopted into APL conjured up difficulties from matrix algebra that were quite unnecessary complications of the elementary notions of list and table.
 

 

Heavy going has often been made of ambivalent functions whose meanings depend upon context, in spite of the fact that such usage (of the minus sign for subtraction in a-b and for negation in -b) is not only commonplace in the most elementary math, but so “natural” as to pass unnoticed by most users.
 

 

The fork (used in the sentence mean=. +/ % #) to define the function mean as the sum divided by the number of items) is sometimes treated as a difficult concept even though it is commonplace in mathematics, as in (f*g) ' for the derivative (denoted by the operator ') of the product of two functions (denoted by the fork f*g). Again, this is a notation so “natural” that (like the ambivalence of -) it commonly passes unremarked.

Even longer lists of seemingly unwarranted difficulties could be compiled in any discipline. Cynics commonly conclude that the “high priests” introduce them deliberately: it is more rational to think that their introduction results from limited tools or lack of experience in early stages, and that their retention results from inattention to difficulties long forgotten.

Smoothing of difficulties requires more than goodwill: a lack of a program or guiding principle may only result in deepening difficulties, due to misguided oversimplification. We will explore the application of two main guides:

1. 

Take a notion perceived as difficult and introduce it early, in simple examples and with a minimum of fanfare. Early introduction allows familiarity with it to develop in a variety of contexts.
 

2. 

Choose a concept that has been introduced late in the development of a discipline, and thoroughly explore its possible (though not necessarily desirable) use in notions introduced earlier.

We will illustrate these approaches by briefly reviewing some early developments in APL, and then explore a treatment of the fork in J.

An adept in APL may be led to bemoan the difficulties of newer notions when compared with such “simple” and “natural” notions as the reduction in APL, without realizing that these APL notions were equally difficult when first encountered. One reason for the difficulty was the scarcity of interesting examples; reduction was first limited to sums and to products of vectors because the appropriate treatment of higher-rank arrays had not been developed, and because such fruitful notions as comparisons and maximum had not yet been adopted as dyadic functions.

Maximum was first introduced in A Programming Language with a logical mask that limited its application to a subset. It was only after development of compression (u/x) and a review of its potential applications that it was realized that it could be used together with reduction by appropriate functions in order to provide the desired masked application of maximum.

Comparisons (such as < and >) were unavailable as dyadic functions for a different reason: following the established convention in mathematics, a phrase such as x<2 was treated as an assertion, not a truth function.

The introduction of the comparisons and further dyadic functions made notions such as reduction and inner and outer product much easier to assimilate, not by simplifying them, but by making them ubiquitous.

The fork was introduced rather late in the development of J, and we will begin by exploring its use in conjunction with just three other notions, name assignment, reduction, and table (outer product):

   a=. 1 2 4        NB. a is the list 1 2 4
   +/a              NB. Reduction
7
   a */ a           NB. Times table
1 2  4
2 4  8
4 8 16
   
   sum=. +/
   sum a
7
   
   mean=. sum % #
   mean a
2.33333
   
   (sum % #) a
2.33333
   sum % # a
0.333333

A beginner might be perplexed by the fact that the last result differs from the preceding two, but this is no more mysterious than the differences among the following:

   b=. 3 + 5
   b * 7
56
   (3 + 5) * 7
56
   3 + 5 * 7
38

In short, parentheses determine the parsing (or order of execution) of a sentence: in the case of sum % # a , the monadic functions number of, reciprocal, and sum are applied in turn, whereas in (sum % #) a , the phrase within the parentheses must be executed first, producing a function that is then applied to the list a .

The definition of a function can be displayed in different forms, and the operator f. can be applied to fix its definition (replacing each component function by its definition). For example:

   mean
┌───┬─┬─┐
│sum│%│#│
└───┴─┴─┘
   9!:3 (5)         NB. Set linear display
   mean
sum % #
   mean f.
+/ % #

Further forks can be used to define functions for centering on the mean and for its square, as follows:

   com=. ] - mean   NB. ] is an identity
   com a
_1.33333 _0.333333 1.66667
   sqcom=. [: *: com
   sqcom a
1.77778 0.111111 2.77778

The function [: is called cap; its effect is to apply the monadic case of the function following it in a fork (in this case, the square *:) . A definition of the standard deviation function can now be completed by applying mean and %: (square root):

   std=. [: %: [: mean sqcom
   std a
1.24722
   std f.
[: %: [: (+/ % #) [: *: ] - +/ % #

Such a definition might be rendered more readable to a beginner by assigning other names to some of the primitives, as follows:

   sqrt=. %:
   sqr =. *:
   id  =. ]
   S=. [:sqrt [:mean [:sqr id-mean
   (std = S) a      NB. A tautology
1

Forks apply dyadically in an obvious way, and the simple dyadic functions [ (left) and ] (right) prove surprisingly useful in dyadic forks:

   a (+ * -) b=. 4 3 2
_15 _5 12
   (a+b) * (a-b)
_15 _5 12
   
   pr=. [ + [: % ]
   3 pr 5           NB. Plus reciprocal
3.2
   pr/3 7 16        NB. Continued fraction
3.14159
   
   7 # 1
1 1 1 1 1 1 1

   pr/7#1           NB. Approximate golden mean
1.61538
   
   pr/\7#1          NB. Convergents
1 2 1.5 1.66667 1.6 1.625 1.61538
   
   into=. ] % [
   5 into 3
0.6
   3 % 5
0.6
   from=. ]-[
   5 from 3
_2

The definitions of from and into illustrate the fact that the fork ]f[ provides the “commute” of the function f . Commutation is important enough to warrant its own adverb (or operator), and the fork ]f[ serves to clarify its definition:

   5 %~ 3
0.6
   5 -~ 3
_2

The dyadic case of the fork [: f g also suggests a useful operator, one that applies the monadic function f at the dyadic function g . For example:

   mad=. [: | -     NB. magnitude atop difference
   a mad b
3 1 2
   
   MAD=. | @: -     NB. The at adverb
   a MAD b
3 1 2

At can be used in an alternate definition of std as follows:

   astd=. %: @: (+/%#) @: *: @: (]-+/%#)
   (std=astd) a
1

We will conclude this exploration of forks by choosing a conjunction (operator that takes two arguments) and attempting to represent it as a fork.

The bond conjunction & (also called currying) bonds a dyadic function with one of its arguments, to produce a monadic function. For example:

   cube=. ^&3       NB. Power with 3
   cube a
1 8 64
   log=. 8&^.       NB. Base eight log
   log a
0 0.333333 0.666667
   8 ^ log a
1 2 4

The attempted fork CUBE=. ] ^ 3 fails for obvious reasons:

   CUBE=. ] ^ 3
   CUBE
20.0855

Each tine of a fork must be a function, and cannot be a noun such as the number three. However, it can be the constant function denoted by 3: , as illustrated below:

   CUBE=. ] ^ 3:
   CUBE a
1 8 64
   LOG=. 8: ^. ]
   LOG a
0 0.333333 0.666667

Other early APL facilities might also be re-examinated in the light of later generalizations. For example, the fruitful scan of APL is commonly introduced by examples such as:

      +\1 2 3 4             ×\1 2 3 4
1 3 6 10             1 2 6 24

The same symbol is used to denote a scan adverb in J, except that it applies to a monadic function (such as sum=. +/) so that the preceding examples would appear as:

   +/\1 2 3 4           */\1 2 3 4
1 3 6 10             1 2 6 24

Monadic functions such as box (<) can be used to good effect to clarify the behaviour of scan:

   <\1 2 3 4
┌─┬───┬─────┬───────┐
│1│1 2│1 2 3│1 2 3 4│
└─┴───┴─────┴───────┘
   <\.1 2 3 4       NB. Suffix scan
┌───────┬─────┬───┬─┐
│1 2 3 4│2 3 4│3 4│4│
└───────┴─────┴───┴─┘
   1 <\. 1 2 3 4    NB. Outfix scan
┌─────┬─────┬─────┬─────┐
│2 3 4│1 3 4│1 2 4│1 2 3│
└─────┴─────┴─────┴─────┘

   ] m=. 4 4 $ 'abcdefghijklmnop'
abcd
efgh
ijkl
mnop

   box=. <"2        NB. Box tables
   toxot=. 1&(|:\.)"2
   box toxot m
┌───┬───┬───┬───┐
│eim│aim│aem│aei│
│fjn│bjn│bfn│bfj│
│gko│cko│cgo│cgk│
│hlp│dlp│dhp│dhl│
└───┴───┴───┴───┘

The function toxot gives the transposed (|:) outfixes of a table (rank 2) argument. Applied twice, it yields all minors of its matrix argument:

   minors=. toxot^:2
   $minors m
4 4 3 3
   <"2 minors m     NB. Box minors
┌───┬───┬───┬───┐
│fgh│egh│efh│efg│
│jkl│ikl│ijl│ijk│
│nop│mop│mnp│mno│
├───┼───┼───┼───┤
│bcd│acd│abd│abc│
│jkl│ikl│ijl│ijk│
│nop│mop│mnp│mno│
├───┼───┼───┼───┤
│bcd│acd│abd│abc│
│fgh│egh│efh│efg│
│nop│mop│mnp│mno│
├───┼───┼───┼───┤
│bcd│acd│abd│abc│
│fgh│egh│efh│efg│
│jkl│ikl│ijl│ijk│
└───┴───┴───┴───┘

Applied twice, minors gives the minors of the minors, best displayed as box^:2 minors^:2 m .



Originally appeared in APL Quote-Quad, Volume 24, Number 3, 1994-03.

created:  2009-11-07 22:05
updated:2013-07-23 22:20