Using J's Boxed Arrays Donald B. McIntyre Luachmhor, Church Road Kinfauns, Perth PH2 7LD Scotland - U.K. Telephone: 0738-86-726 Introduction: APL, which has been an implemented computer language for more than 25 years, grew out of the concise mathematical notation devised by Kenneth Iverson. J is his powerful new dialect [1]. He has defined it in a Dictionary that is an essential reference [2]. Iverson has extended the language in interesting ways. He has also rationalised features which, benefiting from so many years of hindsight, he recognised as anomalies in earlier versions of APL. Users accustomed to APL can therefore expect to be puzzled by changes of behaviour. For example, the sum over a table (+/ table) formerly gave row totals (summing over the last axis), but the same expression in J gives column totals (summing over the first axis). The reason is that the table is considered an array of items, each row being an item. The number of items is given by the tally (#) or by the first number or head ({.) of the shape ($); a 4 by 12 table (matrix) is looked on as a collection of 4 lists (vectors), each of length 12. Consequently the mean (sum divided by tally) is the sum down the columns (across the items) divided by the number of rows. The use of square brackets and semicolon in indexing is anomalous; e.g. expressions such as V[I] or M[I;J] do not have the syntax of other verbs (functions). J uses normal syntax; e.g. i { v (which can be read as "i from v"). In higher rank objects, which need pairs (or triplets, etc) of indexes to identify one atom in the array, the syntax is the same, but indexes identifying a single cell are boxed together. Examples follow. Thinking that my experience with J might help others, I have given accounts of various applications [4-7]. This further example describes one of my first attempts -- the use of boxes to create a small database. Eugene McDonnell responded to my questions in a helpful letter, which I included in my paper for APL91 [4]. Basic Techniques Relating to Boxes:: APL's index generator (iota) has been extended: ]a=. i.2 3 0 1 2 3 4 5 After being boxed, this table of 2 rows (each of length 3) behaves as an atom (scalar): -1- $a 2 3 ]b=. ). wl=. #&.>@;: -2- wl s ÚÄÂÄÂÄÂÄÂÄÂÄÂÄ¿ ³4³2³2³9³4³2³3³ ÀÄÁÄÁÄÁÄÁÄÁÄÁÄÙ If f, g, h are verbs to be applied to a list of numbers, y, then (f g h) y is the same as (f y) g (h y) This train of three verbs is called a fork, and the mean is an example [4-7]. The average word-length is found by applying the verb mean after opening the boxes containing word-lengths. mean=. +/%# mean > wl s 3.71429 Or, defining the verb mwl we get the mean word-length of any string. This is a tacit definition, a pure functional form, with no explicit reference to the arguments. mwl=. mean@(>@wl) NB. Parentheses are required! mwl s 3.71429 When we open a list of boxes we get a table: > ;:s here we go gathering nuts in may The table is alphabetised by the dyadic verb sort (/:) using the adverb both (~) so that the left and right arguments are the same. sort=. /:~@(>@;:) NB. Parentheses are required! sort s gathering go here in may nuts we We must distinguish between boxing the list as a whole and boxing the individual items. The rank conjunction (") instructs the box verb to enclose elements of specified rank (in this case atoms): v=. 2 3.4 5.67 <"0 v ÚÄÂÄÄÄÂÄÄÄÄ¿ ³2³3.4³5.67³ ÀÄÁÄÄÄÁÄÄÄÄÙ The items (in this case individual numbers) are made into vectors (raised from rank-0 to rank-1) by ravel item (,.) ,. v 2 -3- 3.4 5.67 A rank-3 array can be boxed with any rank from 0 to 3: ]m=. i.2 3 4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <"2 m ÚÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄ¿ ³0 1 2 3³12 13 14 15³ ³4 5 6 7³16 17 18 19³ ³8 9 10 11³20 21 22 23³ ÀÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÙ <"1 m ÚÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄ¿ ³0 1 2 3 ³4 5 6 7 ³8 9 10 11 ³ ÃÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄ´ ³12 13 14 15³16 17 18 19³20 21 22 23³ ÀÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÙ -4- Construct a Simple Database: Use "link" (;) to create the database as an array of boxed elements: d=.,:'E.E.';'McDonnell';'Palo Alto';27;10000; 8 3 12 25 10 d=.d,'Ken';'Iverson';'Toronto';55;15000; 4 19 32 1 15 10 d=.d,'Donald';'McIntyre';'U.K.';61;12000;'' d=.d,'Roger';'Hui';'Toronto';49;20000; 32 4 d=.d,'Anthony';'Camacho';'U.K.';45;35000; 19 23 45 4 17 13 5 d ÚÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÂÄÄÂÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³E.E. ³McDonnell³Palo Alto³27³10000³8 3 12 25 10 ³ ÃÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Ken ³Iverson ³Toronto ³55³15000³4 19 32 1 15 10 ³ ÃÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Donald ³McIntyre ³U.K. ³61³12000³ ³ ÃÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Roger ³Hui ³Toronto ³49³20000³32 4 ³ ÃÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Anthony³Camacho ³U.K. ³45³35000³19 23 45 4 17 13 5³ ÀÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ The columns can be taken as First Name, Family Name, Location, Age, and Salary. The final column illustrates a ragged array with one empty cell. (The content of the database is fictitious.) We can extract columns: col=. >@{"1 locality=. 2&col locality d Palo Alto Toronto U.K. Toronto U.K. Pronouns might be convenient: name=. 1 [ age=. 3 [ salary=. 4 Because x is sorted into an order specified by y, x /: y is the same as (/:y) { x See [2]: sort=. ] /: col NB. Fork It is important to remember that right (]) and left ([) are verbs and not merely placeholders; they take arguments and produce results. name sort d ÚÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÂÄÄÂÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³Anthony³Camacho ³U.K. ³45³35000³19 23 45 4 17 13 5³ ÃÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Roger ³Hui ³Toronto ³49³20000³32 4 ³ ÃÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Ken ³Iverson ³Toronto ³55³15000³4 19 32 1 15 10 ³ ÃÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ -5- ³E.E. ³McDonnell³Palo Alto³27³10000³8 3 12 25 10 ³ ÃÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Donald ³McIntyre ³U.K. ³61³12000³ ³ ÀÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ salary sort d ÚÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÂÄÄÂÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³E.E. ³McDonnell³Palo Alto³27³10000³8 3 12 25 10 ³ ÃÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Donald ³McIntyre ³U.K. ³61³12000³ ³ ÃÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Ken ³Iverson ³Toronto ³55³15000³4 19 32 1 15 10 ³ ÃÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Roger ³Hui ³Toronto ³49³20000³32 4 ³ ÃÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Anthony³Camacho ³U.K. ³45³35000³19 23 45 4 17 13 5³ ÀÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ To sort on the basis of the ragged column (5), we might use the mean: mean age col d 47.4 mean salary col d 18400 Using under to define the adverb each each=. &.> meanr=. mean each@(5&{"1) NB. Means in ragged column 5 meanr d ÚÄÄÄÄÂÄÄÄÄÂÄÂÄÄÂÄÄ¿ ³11.6³13.5³0³18³18³ ÀÄÄÄÄÁÄÄÄÄÁÄÁÄÄÁÄÄÙ Sorting on the basis of the means in the ragged column, and appending the means: sortr=. ] /: >@meanr sortr d,"1 0 meanr d ÚÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÂÄÄÂÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄ¿ ³Donald ³McIntyre ³U.K. ³61³12000³ ³0 ³ ÃÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄ´ ³E.E. ³McDonnell³Palo Alto³27³10000³8 3 12 25 10 ³11.6³ ÃÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄ´ ³Ken ³Iverson ³Toronto ³55³15000³4 19 32 1 15 10 ³13.5³ ÃÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄ´ ³Roger ³Hui ³Toronto ³49³20000³32 4 ³18 ³ ÃÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄ´ ³Anthony³Camacho ³U.K. ³45³35000³19 23 45 4 17 13 5³18 ³ ÀÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÙ Append (,"1 0) is performed here so that rank-1 cells (rows) on the left are joined to rank-0 cells (atoms) on the right. To display the data on people at a given locality, apply the adverb each to the verb match (-:). Because the data are boxed, the search-key must be boxed too. Copy (the dyadic #) does the same as compress or replicate in older APL. The adverb cross (~) interchanges the arguments, thus cutting down on parentheses: -6- d #~ >(<'Toronto') -: each 2{"1 d ÚÄÄÄÄÄÂÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÂÄÄÂÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³Ken ³Iverson³Toronto³55³15000³4 19 32 1 15 10³ ÃÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Roger³Hui ³Toronto³49³20000³32 4 ³ ÀÄÄÄÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÁÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ The following verb makes selections easier: place=. ] #~ >@(<@[ -: each 2&{"1 @ ]) 'U.K.' place d ÚÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÂÄÄÄÄÂÄÄÂÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³Donald ³McIntyre³U.K.³61³12000³ ³ ÃÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÅÄÄÄÄÅÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Anthony³Camacho ³U.K.³45³35000³19 23 45 4 17 13 5³ ÀÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÁÄÄÄÄÁÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ -7- Replacement and Insertion: We first assign a "linear index" to each cell: i. $ d 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 li=. [ { i.@$@] NB. Linear Index. Fork 1 4 li d NB. Rows 1 and 4 6 7 8 9 10 11 24 25 26 27 28 29 (<2 5) li d NB. Linear index of cell at row 2 and column 5 17 (2 5;4 2) li d NB. Scattered indexing 17 26 The verb from ({) is used to select information. Placing information into an existing cell is a more complicated process. We use the adverb amend (}), and we need three pieces of information: the array to be changed; the indexes identifying the affected cells; and the new data to be inserted. The indexes will normally be computed from the data, but we can arbitrarily select any cell; e.g. to place an array into the cell at row-2 column-5: x=. 9 8 7 6,:5 4 3 2 (