Vocabulary/VerbRankOld

From J Wiki
Jump to navigation Jump to search
Warning Warning: This page is of historic interest and while we hope it is still useful, it may also include deprecated or obsolete content

See Vocabulary/VerbRank for (hopefully) more current coverage of this topic.

Back to: Vocabulary

Verb Rank

The argument of a verb is allowed to be an array of indefinitely large rank. This is one of J's most conspicuous features.

This ancillary page explains the following concepts

  • verb rank, and how to discover it for any given verb
  • frame and k-cell
  • prefix agreement between x and y arguments
  • how J assembles processed k-cells into an array comprising the result
  • the rank conjunction (or rank operator).

The Verb Rank of a monadic verb

If v is a monadic verb and y is an array of rank n, then (provided J does not signal  domain error),  v y returns a sensible result, no matter how large n is.

But in spite of this feature, every verb has a "preferred" kind of argument, i.e. an array of a particular rank.

Examples:

Verb Syntax Preferred argument y Rank of y Example Result
Negate - y individual number 0 - 5 _5
Do ". y string (=list of bytes) 1 ". '3*5' 15
Matrix Inverse %. y matrix 2 %. 2 2$0 1 2 3 (2 2$_1.5 0.5 1 0)
Ravel , y any array ? , 2 1 3$'abc' 'abcabc'

The rank of a given verb's "preferred" argument is called its verb rank.

Take note of these important extreme cases, which happen to be the most common cases encountered in practice:

  • If the "preferred" argument is a single atom, the verb rank is defined to be 0
  • If the "preferred" argument can be any array, the verb rank is defined to be Infinity (_).

IMPORTANT: When defining your own verb, you need only define it to handle arguments with ranks up to and including the "preferred" rank. J automatically generalizes it to handle arguments of larger rank.

The concept of verb rank generalizes to dyadic verbs in a way we'll explain later.


How to discover the rank of any given verb

Given a verb v, even an anonymous verb, use  b.0 to discover its rank

   NB. Use the examples in the original table
   - b.0
0 0 0
   ". b.0
1 _ _
   %. b.0
2 _ 2
   , b.0
_ _ _

The verb rank as returned by  b.0 is a list of 3 integer atoms

See: Vocabulary/Verbs#Verb_Rank

  • Atom 0 - the monad rank (i.e. of the y-argument)
  • Atom 1 - the left dyad rank (i.e. of the x-argument)
  • Atom 2 - the right dyad rank (i.e. of the y-argument).

(b.0) works not only for primitive verbs, but also for user-defined verbs, whether anonymous or named

   ii b.0
_ _ _
   (] {. [: i. 10 #~ #) b.0
_ _ _

The Rank (") conjunction

The Rank (") conjunction (or rank operator) lets you create a new verb w having a given verb rank r out of an existing verb u

   w =: u " r

Its purpose is to let you apply u individually to any desired splitting into k-cells of a given array y.

You need not assign the new verb a name. You can use it in the form  u"r as you need it.

The right operand r is allowed to take any one of four forms:

1. a list of 3 integers, like that returned by  b.0 as described above 2. a list of 2 integers,  m,n , which is equivalent to the list  n,m,n 3. a single integer, k, which is equivalent to the list  3$k . 4. a verb, v, which is equivalent to the list  v b.0 .

   u =: <           NB. (monad u) Box; (dyad u) Less Than

   w =: u " _ 1 2   NB. 1. above
   w b.0
_ 1 2

   w =: u " 1 2     NB. 2. above
   w b.0
2 1 2

   w =: u " 0       NB. 3. above
   w b.0
0 0 0

   w =: u " ;       NB. 4. above
   w b.0
_ _ _
   ; b.0            NB. w gets the same rank as a given verb: (;)
_ _ _

A common misconception is that Rank (") changes the verb rank of u. It does not.

  • It creates a new verb (u"r) which calls the original u, perhaps several times with different cells as (x-argument and) y-argument, to assemble one single result.
  • At each call, u acts on the argument(s) it sees according to its original verb rank.
    • For an illustration of this behavior, see this worked example ...
      • a verb is created and re-created with different verb ranks, but calls Link (;) internally
      • Link (;) behaves according to its original verb rank (_ _ _) throughout.

The frame and the k-cell

To understand how J applies a given verb v to an array y of rank larger than its preferred argument (as given by its verb rank) we must define two notional arrays: the frame and the k-cell.

Only their shapes are important, and both of these depend on

  • the argument shape — the shape of the y-argument that the verb v is actually called with
  • the value of k — as in: k-cell.

Let s be the argument shape, and let its rank be r. For a given value of k

  • drop the last k atoms of s — this is the frame shape
  • drop the first  0>.(r-k) atoms of s — this is the k-cell shape.

Examples:

k=2
argument shape frame shape k-cell shape
 4 1 5 3 2 4 1 5 3 2

NOTE: The k-cells are matrices.
  

k=1
argument shape frame shape k-cell shape
 4 1 5 3 2 4 1 5 3 2

NOTE: The k-cells are lists.
  

k=0
argument shape frame shape k-cell shape
 4 1 5 3 2 4 1 5 3 2 (empty)

NOTE: An empty k-cell shape means that the k-cells are individual atoms.
  

k=5; k>5
argument shape frame shape k-cell shape
 4 1 5 3 2 (empty) 4 1 5 3 2

NOTE: An empty frame shape means the whole of the y-argument is one single k-cell, and the frame is scalar, which means it is simply its (single) k-cell.


How J applies a monad (v) to its y-argument

We have seen how (b.0) reports the rank of a verb v as a list of 3 atoms. Call these atoms k, m and n

   v =: <   NB. Example: v is the primitive: <

   'k m n' =: v b.0
   k        NB. monad rank
_
   m        NB. dyad left rank
0
   n        NB. dyad right rank
0

For the monad v, the relevant part of the verb rank is k (only).

  • k is the rank of the k-cell to use, in order to partition the shape of y into frame and k-cell
  • m and n are ignored.

To apply the monad v to its right argument y, J follows this three-stage process:

1. the argument y is split up into k-cells, one for each "pigeonhole" of the frame 2. the verb v is applied to each k-cell 3. the individual results are then assembled into an array by putting each back into its "pigeonhole" in the frame.

The three stages are purely notional. In practice the J interpreter takes shortcuts.


A worked example (monad)

Set up a test array to be used as argument y

   ] y =: 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

Note how the verb rank of Box (<) is infinite. Therefore  <y simply boxes y whole and entire

   < b.0   NB. The verb rank of monadic (<) is infinite
_ 0 0

   < y     NB. Therefore Box (<) simply boxes its y-argument whole
+-----------+
| 0  1  2  3|
| 4  5  6  7|
| 8  9 10 11|
|           |
|12 13 14 15|
|16 17 18 19|
|20 21 22 23|
+-----------+

Create verb v using a given value of k (we will do this repeatedly).

k = _
argument shape frame shape k-cell shape
2 3 4 (empty) 2 3 4
   k =: _   NB. rank of k-cells for y-argument to monad v
   m =: 0   NB. don't bother to alter it (unused by monad v)
   n =: 0   NB. don't bother to alter it (unused by monad v)

   v =: < " (k,m,n)
   v  b.0
_ 0 0

But in practice we don't need to bother with m and n since we're only working with the monad v.

So, when specifying the right operand to the conjunction: Rank ("), we only need k, even though this simplified construct changes the dyadic left and right ranks too

   v =: < " k   NB. Specifying an atom k as (") right operand --> list: 3$k
   v  b.0       NB. But only going to use monad v, therefore m, n don't matter
_ _ _

Since k is the original monad rank as shown by  b.0 , we can expect v to behave like the original monad Box (<)

   v y          NB. Original k --> v behaves like (<)
+-----------+
| 0  1  2  3|
| 4  5  6  7|
| 8  9 10 11|
|           |
|12 13 14 15|
|16 17 18 19|
|20 21 22 23|
+-----------+

Now try altering the rank of the k-cells, governing how y is split up.

Setting k=1 makes the k-cells become 1-cells, i.e. lists. The k-cells are now the rows of y:

k = 1
argument shape frame shape k-cell shape
2 3 4 2 3 4
   k =: 1   NB. k-cells are now the rows of y
   v =: < " k
   v y
+-----------+-----------+-----------+
|0 1 2 3    |4 5 6 7    |8 9 10 11  |
+-----------+-----------+-----------+
|12 13 14 15|16 17 18 19|20 21 22 23|
+-----------+-----------+-----------+

Setting k=2 makes the k-cells become 2-cells, i.e. matrices. The k-cells are now the (two) items of y — which happen to be matrices:

k = 2
argument shape frame shape k-cell shape
2 3 4 2 3 4
   k =: 2   NB. k-cells are now matrices
   v =: < " k
   v y
+---------+-----------+
|0 1  2  3|12 13 14 15|
|4 5  6  7|16 17 18 19|
|8 9 10 11|20 21 22 23|
+---------+-----------+

Setting k=3 makes the k-cells become 3-cells, i.e. bricks. Array y itself happens to be a brick, therefore y is (trivially) "split up" into one single k-cell:

k = 3
argument shape frame shape k-cell shape
2 3 4 (empty) 2 3 4
   k =: 3   NB. k-cells are now bricks
   v =: < " k
   v y
+-----------+
| 0  1  2  3|
| 4  5  6  7|
| 8  9 10 11|
|           |
|12 13 14 15|
|16 17 18 19|
|20 21 22 23|
+-----------+

How J applies a dyad (v) to its x-argument and y-argument

Now we extend the foregoing treatment of the monad v to the dyad v.

We have seen how (b.0) reports the rank of a verb v as a list of 3 atoms. Call these atoms k, m and n as before

   'k m n' =: v b.0

For the dyad v, the relevant parts of the verb rank are m for the x-argument, and n for the y-argument.

  • k is ignored
  • m is the rank of the m-cell to use, in order to partition the shape of x into x-frame and m-cell
  • n is the rank of the n-cell to use, in order to partition the shape of y into y-frame and n-cell.

To apply the dyad v to its right argument y, and its right argument y, J follows this three-stage process:

1. the argument x is split up into m-cells and the argument y is split up into n-cells 2. the verb v is applied between corresponding pairs of m-cell and n-cell 3. the individual results are then assembled into an array by putting each back into its "pigeonhole" in the common frame.

As before, the three stages are purely notional. In practice the J interpreter takes shortcuts.

This process will fail with  length error unless x and y prefix-agree, i.e.

  • the x-frame shape is a prefix of the y-frame shape
  • or vice-versa.

Assuming that x and y prefix-agree, what frame is used to assemble the results? J follows these notional steps with the x-frame and y-frame to arrive at a common frame

  • they are first brought to a common rank by introducing leading unit axes to whichever frame has the lower rank
  • they are then brought to a common shape by padding with the appropriate fill element (typically 0 for a numeric array).

Simple cases are the commonest

These simple cases happen to be the commonest cases, so prefix-agreement rarely needs to be applied in its full generality.

1. The x-frame shape is the y-frame shape.

  • Without loss of generality, the common frame can then be taken as the y-frame.
  • Corresponding pairs of m-cell (xm) and n-cell (yn) thus have the same "pigeonhole" in the common frame.
  • Then individual results are all the result of  xm v yn , to be put back into that "pigeonhole".

2. The x-frame shape is the empty vector ('') This always happens when the x-rank of v is infinite.

  • It follows that the whole of x is a single m-cell.
  • The common frame is therefore the y-frame.
  • Then individual results are all the result of  x v yn , to be put back into the "pigeonhole" (yn) in the y-frame.

3. Same as 2. above, but x and y are swapped.


Simple examples (dyad)

Case 1. above

   ] y =: i. 2 3
0 1 2
3 4 5

   ] x =: -y   NB. (x-frame shape) -: (y-frame shape)
 0 _1 _2
_3 _4 _5

   v =: j.     NB. forms a single atom showing its 2 arguments

   x v y
   0 _1j1 _2j2
_3j3 _4j4 _5j5

Case 2. above

   ] y =: i. 2 3
0 1 2
3 4 5

   x =: 0.01

   v =: +      NB. (here) forms a single atom showing its 2 arguments

   x v y
0.01 1.01 2.01
3.01 4.01 5.01

Case 3. above

   y v x
0.01 1.01 2.01
3.01 4.01 5.01

A more general worked example (dyad)

Set up test arrays in y and x

   ] y =: i. 2 3
0 1 2
3 4 5

   ] x =: 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

Note how the verb rank(s) of Link (;) are all infinite. Therefore  x;y simply links x and y whole and entire

   ;  b.0
_ _ _

   x ; y
+-----------+-----+
| 0  1  2  3|0 1 2|
| 4  5  6  7|3 4 5|
| 8  9 10 11|     |
|           |     |
|12 13 14 15|     |
|16 17 18 19|     |
|20 21 22 23|     |
+-----------+-----+

Create verb v using given values of k, m and n (we will do this repeatedly).

m = _
x-argument shape x-frame shape m-cell shape
2 3 4 (empty) 2 3 4
n = _
y-argument shape y-frame shape n-cell shape
2 3 (empty) 2 3

Prefix agreement: Trivially, the x-frame shape is a prefix of the y-frame shape (both are empty).

Since k, m and n are the original verb ranks as shown by b.0 then we can expect v to behave like the original Link (;)

   k =: _   NB. Don't bother to alter it (unused by dyad v)
   m =: _   NB. rank of m-cells for x-argument to dyad v
   n =: _   NB. rank of n-cells for y-argument to dyad v

   v =: ; " (k,m,n)   NB. Original (k,m,n) --> v behaves like (;)

   x v y
+-----------+-----+
| 0  1  2  3|0 1 2|
| 4  5  6  7|3 4 5|
| 8  9 10 11|     |
|           |     |
|12 13 14 15|     |
|16 17 18 19|     |
|20 21 22 23|     |
+-----------+-----+

Alter the rank of the n-cells, i.e. the cells into which y is split up.

m = _
x-argument shape x-frame shape m-cell shape
2 3 4 (empty) 2 3 4
n = 1
y-argument shape y-frame shape n-cell shape
2 3 2 3

Prefix agreement: Trivially, the (empty) x-frame shape is a prefix of the y-frame shape (2)

   n =: 1   NB. n-cells are now the rows of y
   v =: ; " (k,m,n)
   x v y
+-----------+-----+
| 0  1  2  3|0 1 2|
| 4  5  6  7|     |
| 8  9 10 11|     |
|           |     |
|12 13 14 15|     |
|16 17 18 19|     |
|20 21 22 23|     |
+-----------+-----+
| 0  1  2  3|3 4 5|
| 4  5  6  7|     |
| 8  9 10 11|     |
|           |     |
|12 13 14 15|     |
|16 17 18 19|     |
|20 21 22 23|     |
+-----------+-----+

Now, leaving the n-cells unchanged, alter the rank of the m-cells, i.e. the cells into which x is split up:

m = 1
x-argument shape x-frame shape m-cell shape
2 3 4 2 3 4
n = 1
y-argument shape y-frame shape n-cell shape
2 3 2 3

Prefix agreement: The y-frame shape (2) is a prefix of the x-frame shape (2 3).

   m =: 1   NB. m-cells are now the rows of x
   v =: ; " (k,m,n)
   x v y
+-----------+-----+
|0 1 2 3    |0 1 2|
+-----------+-----+
|4 5 6 7    |0 1 2|
+-----------+-----+
|8 9 10 11  |0 1 2|
+-----------+-----+

+-----------+-----+
|12 13 14 15|3 4 5|
+-----------+-----+
|16 17 18 19|3 4 5|
+-----------+-----+
|20 21 22 23|3 4 5|
+-----------+-----+

Now, still leaving the n-cells unchanged, try a different rank for the m-cells, viz. m = 2, to make them matrices:

m = 2
x-argument shape x-frame shape m-cell shape
2 3 4 2 3 4
n = 1
y-argument shape y-frame shape n-cell shape
2 3 2 3

Prefix agreement: Trivially, the y-frame shape is a prefix of the x-frame shape (both are 2).

   m =: 2   NB. m-cells are now matrices
   v =: ; " (k,m,n)
   x v y
+-----------+-----+
|0 1  2  3  |0 1 2|
|4 5  6  7  |     |
|8 9 10 11  |     |
+-----------+-----+
|12 13 14 15|3 4 5|
|16 17 18 19|     |
|20 21 22 23|     |
+-----------+-----+