Revisiting Rough Spots
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:
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:
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.