Skip to content

APL2 and J (2) - Indexing

Vector 13:2, page 137

language design programming techniques

Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 137

This series of articles entitled APL and J will try to explain the J way of thinking, by contrasting APL and J coding examples. The term APL will be used generically to refer to facilities found in most commercial implementations of APL.

Here we look at how J handles indexing and indexed replacement.

Housekeeping

We use the letters V, M, and A for vectors, matrices and arrays of any dimension, optionally followed by numbers to denote their shape. Thus V for any vector, V5 for a vector of length 5, M45 for a 4-by-5 matrix, A245 for a 2-by-4-by-5 array. Where actual values are required, V5 will be defined as ⍳5, M45 as 4 5⍴⍳20, for example:

      M45
 0  1  2  3  4
 5  6  7  8  9
10 11 12 13 14
15 16 17 18 19

APL terminology will be used, except where J terminology is required for clarity. Since J uses origin 0 only, here ⎕io←0 is assumed throughout.

Indexing

In APL, you index variables using an indexing expression in brackets. Such expressions are not supported in J; instead, J uses a function { from. Compare the following expressions to select row 2 of a matrix:

M[2;] APL
2{M J

To understand this change, note that bracket indexing in APL does not conform to the rest of the APL language. Indexing is essentially a dyadic function taking arguments of the data and indices, yet the APL expression does not have the form of other APL dyadic functions (i.e. a function with arguments to the left and right).

Moreover you cannot use the APL expression as you would a normal function. For example if N is an enclosed array, each item of which is a matrix, you could not use bracket indexing to select row 2 of each item:

      N ¨ [2;]
SYNTAX ERROR

If you wanted to do this, you could define a cover function to index a matrix:

    ∇ r←ndx rowndx mat
[1]   r←mat[ndx;]
    ∇

You could then select row 2 of M as:

      2 rowndx M

and row 2 of each element of N as:

      2 rowndx¨ N

The corresponding J expressions are:

   2 { M
   2 {each N

It may be seen that { is simply a functional form of bracket indexing. Because it is an ordinary function, you can use it in functional expressions, as in the example above. The idea of { is similar, but not identical, to the index function ⌷ in APL2.

Note that in the above example, should you wish to index row 2 of a 3D array, you would need to modify rowndx accordingly, but the J expression would remain unchanged.

Here are a couple more examples:

   item2=. 2&{                function to return item 2

   item2 M45
10 11 12 13 14

   item2 A324
16 17 18 19
20 21 22 23
   mrow=. {&M45               function to return rows of M45

   mrow 3 2
15 16 17 18 19
10 11 12 13 14

From Left Argument

The left argument can take various forms:

  1. It can be one or more integers, which select corresponding items from the right argument. Negative integers select from the end, for example:

       2 _1 { M45             row 2 and last row of M45
    10 11 12 13 14
    15 16 17 18 19
    

    It is often convenient to apply rank with this form, for example:

       2 _1 {"1 M45           column 2 and last column of M45
     2  4
     7  9
    12 14
    17 19
    
  2. The left argument can be one or more boxed lists, each element of which is a list of integers that select from corresponding axes:

       M45[1;3]               APL
    8
       (<1 3){M45             J
    8
    

    This form allows scattered indexing:

       M45[(1 3)(2 4)]        APL2 and Dyalog APL
    8 14
       (1 3;2 4){M45          J
    8 14
    
  3. The boxed lists in the left argument may themselves contain boxed lists, each element again selecting from corresponding axes:

       M45[1;3 4]             APL
    8 9
       (<1;3 4){M45           J
    8 9
    

Note that in J, this last form permits scattered indexing where each index may have a different number of elements. The following example has two indices: the first picks row 1, columns 3 and 4; the second picks row 2, column 3. The two results are assembled into a matrix, padding the second row with a fill of 0:

   ((1;3 4);2 3){M45
 8 9
13 0

Selecting Entire Axis

In APL, when an axis is elided, the entire axis is selected. This is not the same as using an empty index:

      M45[;2]                 pick all rows, column 2
2 7 12 17

      M45['';2]               pick no rows, column 2

In J, the elided axis is given by the boxed empty vector. Thus:

   (<(<'');2){M45             pick all rows, column 2
2 7 12 17

   (<'';2){M45                pick no rows, column 2

Here, it is convenient to use a: ace, which is the boxed empty vector:

   (<a: ;2){M45               pick all rows, column 2
2 7 12 17

In general, a boxed argument used as an index selects all but the indices given. So, the boxed empty means select all but the empty, i.e. select all. The following picks all but the first and last row from columns 2 and 3:

   (<(<0 _1);2 3){M45
 7  8
12 13

Indexed Replacement

As with indexing, indexed replacement in APL does not conform to the rest of the language. It requires three arguments – a set of indices, and two sets of data, at least one of which must be named. The named data item and the indices are given to the left of assignment, and the other data item to the right:

      M[2;] ← V               replace row 2 of M with V

Indexed replacement is not a functional expression. The "result" of indexing is the values assigned (V in the above example), so that M is updated as a side effect.

In contrast, indexed replacement in J is an ordinary part of the language. It has two steps:

  • the indices to be replaced are given as the argument to the adverb } amend, returning a dyadic function that merges its two arguments according to the indices.
  • the function is then applied to the two data arguments. The elements of the left argument replace the elements of the right argument according to the indices.

For example, to replace row 2 of M with V:

   V 2} M

The order of execution is shown by:

   V (2}) M                   2} is a function that merges item 2

To replace row 2 of each item of a boxed list of matrices:

   (<V) 2} each N

Note that indexed replacement in J does not require assignment and does not update the arguments, so there is no need for a named argument. The following updates M:

   M=. V 2} M

Arguments To Amend

The index arguments to } are symmetrical with index arguments to {. For example:

   2{M45                                select row 2
10 11 12 13 14

   (100*i.5) 2} M45                     amend row 2
 0   1   2   3   4
 5   6   7   8   9
 0 100 200 300 400
15  16  17  18  19

   (<1;1 2 3){M45                       select row 1 columns 1 2 3
6 7 8

   100 200 300 (<1;1 2 3)} M45          amend row 1 columns 1 2 3
 0   1   2   3  4
 5 100 200 300  9
10  11  12  13 14
15  16  17  18 19

Selective Assignment

Modern APLs can replace a portion of an array selected by an expression given to the left of assignment, this is called selective assignment. For example, the following replaces all occurrences of 2 in V by 20:

      V←1 2 2 3 1 2
      ((V=2)/V) ← 20
      V
1 20 20 3 1 20

Similar facilities in J are provided by using a function argument to amend, which function is used to create the required indices. Thus:

   bx 1 0 1 1 0 1 1           the utility bx returns indices of a boolean
0 2 3 5 6

   f=. bx @ (2: =])           f returns indices where right argument is 2

   20 f} 1 2 2 3 1 2          use f to replace 2 by 20
1 20 20 3 1 20

Fetch

A new function in J3.02 is {:: fetch which when used dyadically as in x fetch y returns the subarray of y according to the path x; the selection at each level is based on {, for example:

   [A=. 1 2 3;4 5;i.4 5
┌─────┬───┬──────────────┐
│1 2 3│4 5│ 0  1  2  3  4│
│     │   │ 5  6  7  8  9│
│     │   │10 11 12 13 14│
│     │   │15 16 17 18 19│
└─────┴───┴──────────────┘

   (2;_1 _1) fetch A          fetch from item 2, the element in the last row
19                            and column

   (2;<2;2 3 4) fetch A       fetch from item 2, the elements indexed by 2;2 3 4
12 13 14

This is similar to the reach facility of Dyalog APL, except that in J the final selection need not be a scalar:

      A[(2(2 2))(2(2 3))(2(2 4))]
12 13 14

Reference

Iverson, K.E. J Introduction and Dictionary, Iverson Software, 1996