>>  <<  Ndx  Usr  Pri  JfC  LJ  Phr  Dic  Rel  Voc  !:  wd  Help  J for C Programmers

                                                                                        27. Modifying an array: m}

Modification of a portion of an array, performed in C by the simple expression x[m] = y, fits uneasily into J.  To begin with, 3 pieces of information are needed: the array x, the index m, and the replacement data .  Since J verbs take only 2 operands, that means the primitive to modify the array will be an adverb so that its left operand can hold one of the 3; the verb derived from the adverb and its operand will perform the modification.

Moreover, the adverb cannot be a modifier of the array's name as [m] is a modifier of x in x[m] = y.  In J, the array x may not have a name.  While in C every array is declared and named, in J we summon up anonymous arrays in the blink of an i., use them, and let them disappear.  We expect modification of a subarray to be like other primitives in this regard, which means that it must not be a form of assignment to a name using a copula (=. or =:), but instead a (derived) verb operating on an array to produce a result.

What will the result of the assignment be?  This is not much of an issue in C.  There the result of the assignment is y, but it is seldom used: in C, after we have employed x[m] = expression; we are usually content to put down the pencil, satisfied that we have described a statement's worth of computation.  In J we routinely use weapons of larger bore: after we have modified the array we expect to be able to sort it, or add it to another array, or the like.  It is clear that the result of the modification of the array must be the entire modified array.

This reasoning justifies J's design.  } is an adverb.  Modification of an array is performed by dyad m} which produces a derived verb of infinite rank.  x m} y creates a copy of y and installs the atoms of x into the positions selected by .  m specifies portions of y in the same way as the left operand of m{y .  Even though dyad { has left rank 0, m may have any shape: the atoms of m are processed to accumulate a list of selected portions of .  The shape of x must be a suffix of the shape of m{y, in other words x must either have the same shape as the selected portion(s) m{y or have the same shape as some cell of m{y in which case x is replicated to come up to the shape of m{y .  Examples are a recap of the forms of the left operand of dyad {, with a couple of twists:

   0 (<1 1)} 4 4 $ 5

5 5 5 5

5 0 5 5

5 5 5 5

5 5 5 5

The simplest example.  We select an atom and modify it.

   0 1 2 3 (0)} 4 4 $ 5

0 1 2 3

5 5 5 5

5 5 5 5

5 5 5 5

Here we are modifying a selected row.

   0 (0)} 4 4 $ 5

0 0 0 0

5 5 5 5

5 5 5 5

5 5 5 5

x is shorter than m{y but x can be replicated to match the shape of m{y .

   0 1 (0)} 4 4 $ 5

|length error

|   0 1    (0)}4 4$5

Here the shape of x is 2 while the shape of m{y is 4, so the operands do not agree.

   1 2 (1 1;2 2)} 4 4 $ 5

5 5 5 5

5 1 5 5

5 5 2 5

5 5 5 5

Here m has 2 atoms, each selecting a single atom of .  x has two atoms and they are stored into the selected positions.

   1 2 (1 1;1 1)} 4 4 $ 5

5 5 5 5

5 2 5 5

5 5 5 5

5 5 5 5

The same element is modified twice.  As it happens, the modifications are performed in order, and the last one survives in the result.  Do not rely on this behavior!  As a general rule in J, the order in which a parallel operation is performed is undefined and may change from release to release or from machine to machine.

The portions of y selected by each atom of m must have the same shape.

Monad I.--Indexes of the 1s in a Boolean Vector

If you have a Boolean list with 1s representing items to be modified, you will need to create the list of indexes of the 1s (for selection you would just use x # y, but for modification you must use m} which needs the indexes rather than the Boolean list).

Monad I. (rank 1) performs this function.  I. y produces y # i. # y, for example:

   I. 1 0 0 1 0 1 1

0 3 5 6

indicating where the 1s are.

You should use I., rather than any equivalent phrase, to perform this function, because the interpreter recognizes compounds such as I.@:> and handles them with special fast code.

Modification In Place

It is fundamental to the design of J that, in general, verbs produce output in a separate memory block from the inputs.  Thus, if I execute >: y, the result will be in a different memory area from .  This takes more space than incrementing y in place, but not much additional computation, since every atom of y must be visited and incremented either way.  Usually the extra memory usage is immaterial to overall performance.  Sometimes we regret having to make a new copy for the output, for example when we just remove the end of a long list with }: y or add a single character to the end of a long string with x , ' ' .

Profligacy with memory bandwidth reaches an extreme with dyad m} : even if y is huge and only one atom is modified, the whole y must be copied to produce the result.  Sometimes this can be a noticeable drag on performance.  To help out in those cases, the interpreter recognizes the specific forms

name =. x m} name

name =: x m} name

name =. name , x

name =: name , x

and executes the modification in place, i. e. without creating a new copy of name .  For the modification to be in-place, there must be nothing else in the sentence either before or after the form shown above, and the two appearances of name must be identical.  x and m may be any expressions that evaluate to nouns.


>>  <<  Ndx  Usr  Pri  JfC  LJ  Phr  Dic  Rel  Voc  !:  wd  Help  J for C Programmers