>>  <<  Usr  Pri  JfC  LJ  Phr  Dic  Rel  Voc  !:  Help  Learning J

Chapter 13: Explicit Operators

This chapter covers explicit definition of operators, that is, adverbs and conjunctions defined with the colon conjunction.

The scheme for explicit definition is:

            1 : body     is an adverb
            2 : body     is a conjunction
where body is one or more lines of text. The possibilities for the result produced by an operator so defined are: a tacit verb, an explicit verb, a noun or another operator. We will look at each case in turn.

13.1 Operators Generating Tacit Verbs

Recall from Chapter 07 the built-in rank conjunction ". For any verb u, the expression u"0 is a verb which applies u to the 0-cells (scalars) of its argument.

Now suppose we aim to define an adverb A, to generate a verb according to the scheme: for any verb u

         u A   is to be     u " 0
Adverb A is defined explicitly like this:

A =: 1 : 'u " 0' f =: < A f 1 2
1 : 'u " 0' <"0 +-+-+

In the definition (A =: 1 : 'u " 0') the left argument of the colon is 1, meaning "adverb".

The right argument is the string 'u " 0'. This string has the form of a tacit verb, where u stands for whatever verb will be supplied as argument to the adverb A. In the explicit definition of an adverb, the argument-variable is always u.

Adverbs are so called because, in English grammar, adverbs modify verbs. In J, by contrast, adverbs and conjunctions in general can take nouns or verbs as arguments. In the following example, adverb W is to generate a verb according to the scheme: for integer u

        u W    is to be   < " u
that is, u W boxes the rank-u cells of the argument. The definition of W is shown by:

W =: 1 : '< " u' 0 W z =: 'abc' 0 W z 1 W z
1 : '< " u' <"0 abc +-+-+-+

For another example of an adverb, recall the dyad # where x # y selects items from y according to the bitstring x.

y =: 1 0 2 3 1 0 1 1 # y
1 0 2 3 1 2 3

To select items greater than 0, we can apply the test-verb (>&0)

y >&0 y (>&0 y) # y
1 0 2 3 1 0 1 1 1 2 3

A tacit verb to select items greater than 0 can be written as a fork f:

f =: >&0 # ] f y
>&0 # ] 1 2 3

This fork can be generalised into an adverb, B say, to generate a verb to select items according to whatever verb is supplied in place of the test >&0.

   B =: 1 : 'u # ]'
If we supply >&1 as a test-verb:

g =: (>&1) B y g y
>&1 # ] 1 0 2 3 2 3

We see that the body of B is the fork to be generated, with u standing for the argument-verb to be supplied. Conjunctions, taking two arguments, are defined with (2 : '...'). The left argument is u and the right is v

For example, consider a conjunction THEN, to apply one verb and then apply another to the result, that is, a composition. The scheme we want is:

           u THEN v     is to be v @: u
and the definition of THEN is:

THEN =: 2 : 'v @: u' h =: *: THEN < h 1 2 3
2 : 'v @: u' <@:*: +-----+
|1 4 9|

For another example, consider counting (with #) those items of a list which are greater than 0. A verb to do this might be:

foo =: # @: (>&0 # ]) y foo y
#@:(>&0 # ]) 1 0 2 3 3

We can generalize foo to apply a given verb u to items selected by another given verb v. We define a conjunction C with the scheme

             u C v   is to be   u @: (v # ]) 
and the definition of C is straightforwardly:

C =: 2 : 'u @: (v # ])' f =: # C (>&0) y f y
2 : 'u @: (v # ])' #@:(>&0 # ]) 1 0 2 3 3

13.1.1 Multiline Bodies

The right argument of colon we may call the body of the definition of our operator. In the examples so far, the body was a string, a schematic tacit verb, for example 'v .@: u' . This is the verb to be delivered by our operator. More generally, the body can be several lines. The idea is that, when the operator is applied to its argument, the whole body is executed. That is, each line is evaluated in turn and the result delivered is the value of the last line evaluated. This is exactly analogous to explicit verbs, except that here the result is a value of type "function" rather than of type "array".

Here is an example of a multi-line body, the previous example done in two steps. To apply u to items selected by v, a scheme for conjunction D could be written:

         u D v  is to be  (u @: select) where select is v # ] 
and D defined by
   D =: 2 : 0
select =: v # ]
u @: select
Again counting items greater than 0, we have

f =: # D (>&0) y f y
#@:select 1 0 2 3 3

The first line of D computes an inner function select from the right argument. The second line composes select with the left argument, and this is the result-verb delivered by D.

Now this definition has an undesirable feature: we see that select is defined as a global (with =:). It would be better if select were local.

However, we can see, by looking at the value of the verb f above, that select must be available when we apply f. If select is local to D, it will not be available when needed.

We can in effect make select local by using the "Fix" adverb (f.) (letter-f dot.) The effect of applying "Fix" to a verb is to produce an equivalent verb in which names are replaced by by their corresponding definitions. That is, "Fix" resolves a tacit verb into its primitives. For example:

p =: + q =: * r =: p,q r f.
+ * p , q + , *

Here is how we use Fix to enable select to be local. In the example below, notice that we Fix the result-expression on the last line:

   E =: 2 : 0
select =. v # ]
(u @: select) f.
Now a verb to count greater-than-0 items can be written:

g =: # E (>&0) y g y
#@:(>&0 # ]) 1 0 2 3 3

We see that g, unlike f, has no local names.

13.2 New Definitions from Old

Suppose we aim to develope a function which, given a list of numbers, replaces each number by the mean of its two neighbours in the list, the previous and the next. For the first or last, we assume a neighbour is zero.

A suitable "data smoothing" function could be written

   sh    =: |. !. 0    NB. shift, entering zero
   prev  =: _1 & sh    NB. right shift
   next  =: 1 & sh     NB. left shift
   halve =: -:
   smoo  =: halve @: (prev + next)
so for a list of numbers N we might see :

N =: 6 2 8 2 4 prev N next N smoo N
6 2 8 2 4 0 6 2 8 2 2 8 2 4 0 1 7 2 6 1

Now suppose we also want another smoothing function which rotates the data rather than shifting in zero. (The data might be, say, samples of a repeated waveform.)

The only change needed from smoo is that the shift verb sh must become a rotate verb, that is, (|.).

If the definition of smoo were large and complicated we might prefer to avoid entering it again. Instead, we could re-evaluate the definition we already have, in an environment in which the name sh means "rotate". This environment can be conveniently provided by a little adverb, SMOO say, with |. (rotate) for its argument:

   SMOO =: 1 : ('sh =. u' ; 'smoo f.')
so the rotating variant of smoo is given by
   rv =: |. SMOO
-:@:(_1&|. + 1&|.)

N smoo N rv N
6 2 8 2 4 1 7 2 6 1 3 7 2 6 4

This example shows using an adverb to generalise an expression (smoo) to a function In summary, since smoo is defined in terms of sh, we have generalised it to a function taking sh as argument.

13.3 Operators Generating Explicit Verbs

Suppose we aim to define a conjunction H say, with the scheme:
  u H v    is to be     3 : 0
                        z =. v y
                        y u z
There is a messy way and a neat way to do this. Let me show you the messy way first, so that the merits of the neat way can be appreciated.

The messy way: we can write H in the same style as the previous examples. That is, the body of the definition computes a value which is delivered when the operator is applied to arguments. In this case the value is to be of the form 3 : string where string must be built from the arguments. For example:

   H =:  2 : 0
    U =. 5!:5 < 'u'
    V =. 5!:5 < 'v'
    string =. 'z =. ', V , 'y',  LF
    string =. string , 'y ', U , ' z', LF
    3 : string
and we see
   foo =: + H *:
   foo 5
The conjunction H is pretty ugly but the value of the generated function foo is plain to see:
3 : 0
z =. *:y
y + z
Now we come to the neat way to define this conjunction. So far we have seen operators where the body is executed to deliver the result. Let us say they are operators of the first kind. Now we look at operators of the second kind, where the body of the operator is not executed but instead serves as a template for the verb to be generated. For example:
   K =: 2 : 0
z =. v y
y u z
Clearly the definition of K is neater than the definition of H but nevertheless they are equivalent. Notice that the body of K contains both the argument-variables u and v for the operator, and also the argument-variable y of the generated verb. It is this combination of argument variables which determines that the operator is of the second kind.
   bar =: + K *:
   bar 5
The generated verb bar is equivalent to foo but it is displayed differently.
+ (2 : 0) *:
z =. v y
y u z
Now we look in more detail at examples of operators of the second kind.

13.3.1 Adverb Generating Monad

Consider the following explicit monadic verb, e. It selects items greater than 0, by applying the test-verb >&0.

e =: 3 : '(>&0 y) # y' y e y
3 : '(>&0 y) # y' 1 0 2 3 1 2 3

We can generalise e to form an adverb, F say, which selects items according to a supplied test-verb. The scheme we want is: for any verb u:

         u F   is to be    3 : '(u y) # y'
Adverb F is defined by:
   F  =: 1 : '(u y) # y'
Now the verb >&1 F will select items greater than 1:

y >&1 F y
1 0 2 3 2 3

In the body of F the variable u stands for a verb to be supplied as argument to adverb F. If this argument is say >&1, then y stands for an argument to the generated explicit verb 3 : '(>&1 y) # y'.

That is, our method of defining the generated verb is to write out the body of an explicit definition, with u at places where a supplied verb is to be substituted.

13.3.2 Adverb Generating Explicit Dyad

Suppose we want an adverb W, say, with the scheme: for any verb u

           u W     is to be      4 : '(u x) + (u y)'
Recall from Chapter 12 that there is another way to write an explicit dyad. Rather than beginning with 4 : we can begin with 3 : and write a multi-line body in which a solo colon separates monadic and dyadic cases. Here we have no monadic case, so the scheme above can be equivalently written as:
           u W    is to be     3 : 0
                               (u x) + (u y)
The explicit definition of adverb W follows straightforwardly:
   W =: 1 : 0
(u x) + (u y)
We see:

(*: 2) + (*: 16) 2 (*: W) 16
260 260

For another example, suppose we want an adverb, T say, to apply a given verb u to every combination of a scalar in vector argument x with a scalar in vector argument y. There is a built-in adverb / called "Table" for this, but here is a home-made version. The scheme is:

           u T   is to be 4 : ' x (u " 0 0) " 0 1  y' 
that is,
           u T   is to be 3 : 0
                          x  (u " 0 0) " 0 1  y
and so T is defined by
   T =: 1 : 0
x  ((u " 0 0) " 0 1)  y
so we see:
   1 2 3 + T 4 5 6 7
5 6 7  8
6 7 8  9
7 8 9 10

13.3.3 Conjunction Generating Explicit Monad

A conjunction takes two arguments, called u and v.

As before, we specify the generated verb by writing out the body of an explicit verb. Here y stands for the argument of the generated verb and u and v stand for argument-verbs to be supplied to the conjunction. In this example the body is multi-line. As before, u will be applied to items selected by v

   G  =: 2 : 0
selected =. (v y) # y
u selected
Now a verb to count greater-than-zero items can be written as # G (>&0):

y # G (>&0) y
1 0 2 3 3

13.3.4 Generating a Explicit Dyad

Suppose we want a conjunction H such that, schematically, for verbs u and v
         u H v    is to be    4 : '(u x) + (v y)'
or equivalently, as we saw above:
         u H v    is to be    3 : 0
                              (u x) + (v y)
The explicit definition of H follows straightforwardly:
   H =: 2 : 0
(u x) + (v y)
We see:

(*: 2) + (%: 16) 2 (*: H %:) 16
8 8

13.3.5 Alternative Names for Argument-Variables

For the sake of completeness, it should be mentioned that arguments to operators may be named m and n rather than u and v, to constrain arguments to be nouns, that is, to cause verbs to be signalled as errors.

Furthermore, for historical reasons, if the only argument variables are x or y or both, we get an operator of the first kind. That is, in the absence of u or v or m or n then x and y are equivalent to u and v.

These alternative names will not be further considered.

13.3.6 Review

So far, we have seen that for operators introduced with 1 : body or 2 : body, there are two kinds of definition.

  • With operators of the first kind, the body is executed (that, is evaluated) to compute the value of the result. The result can be of any type. The argument-variables occurring in the body are u or v or both.

  • With operators of the second kind, the result is always an explicit function. The body of the operator is not executed, but rather becomes the body of the generated function. Here x and y are arguments to the generated function in the usual way, and u or v in this body are placeholders which receive the values ofarguments to the operator.
The J system recognises which kind is intended by determining which of the argument-variables u v x y occur in the the body. If we If we have BOTH (u or v) AND (x or y) then the operator is of the second kind. Otherwise it is of the first kind.

13.3.7 Executing the Body (Or Not)

We said above that, for an operator of the first kind, the body is executed (or evaluated) when arguments are supplied. This can be demonstrated.

First, here is a utility verb which displays its argument on-screen.

   display =: (1 !: 2) & 2
Now insert display 'hello' into an operator of the first kind:
     R =: 2 : 0
display 'hello'
select =. v # ]   
(u @: select) f.
When R is applied to its argument, the body is demonstrably executed:
   f =: # R (>&0)
   f 1 0 2 0 3
By contrast, for an operator of the second kind, when arguments are supplied, the body is not executed, but rather becomes the body of the result function (after substituting the arguments). We can demonstrate this by inserting display 'hello' into the body of the operator, and observing that it becomes part of the result-function.
   S =: 2 : 0
display 'hello'
selected =. (v y) # y  
u selected
we see that the body of S is NOT executed when S is applied to its argument, but it IS executed when the generated verb g is applied.
   g =: # S (>&0)
   g 1 0 2 0 3

13.4 Operators Generating Nouns

Operators can generate nouns as well as verbs. Here is an example.

A fixed point of a function f is a value p such that (f p) = p. If we take f to be

   f =: 3 : '2.8 * y * (1-y)'
then we see that 0.642857 is a fixed-point of f
   f 0.642857
Not every function has a fixed point, but if there is one we may be able to find it. We can iterate the function until there is no change (with ^: _ - see Chapter 10), choosing a suitable starting value. A crude fixed-point-finder can be written as an adverb FPF which takes the given function as argument, with 0.5 for a starting value.

FPF =: 1 : '(u ^: _ ) 0.5' p =: f FPF f p
1 : '(u ^: _ ) 0.5' 0.642857 0.642857

13.5 Generating Noun or Verb

Consider two lines of J, such as
               sum  =: +/
               mean =: sum  % #
Sometimes a smoother presentation might be:
               mean =: sum % #  where sum =: +/
provided we had available a suitable definition for where. How about this?
   where =: 2 : 'u'
so we can say:
   mean =: sum % # where sum =: +/
with results as expected:

mean mean 1 2 3 4
sum % # 2.5

The right argument of where can be a verb or noun:

    (z+1) * (z-1)     where z =: 7
where is a conjunction which ignores its right argument, but evaluating the right argument makes it available to the left through the assignment. Note that the assignments to sum and z above are regular global assignments, so where does not localize sum or z.

13.6 Operators Generating Operators

Here is an example of an adverb generating an adverb.

First note that (as covered in Chapter 15) if we supply one argument to a conjunction we get an adverb. The expression (@: *:) is an adverb which means "composed with square". To illustrate:

CS =: @: *: - CS - CS 2 3 - *: 2 3
@:*: -@:*: _4 _9 _4 _9

Now back to the main example of this section. We aim to define an explicit adverb, K say, which generates an adverb according to the scheme: for a verb u

          u K    is to be     @: u
Adverb K can be defined as below. We see that adverb K delivers as a result adverb L:

K =: 1 : '@: u' L =: *: K - L - L 2 3
1 : '@: u' @:*: -@:*: _4 _9

This is the end of Chapter 13.

Table of Contents

The examples in this chapter were executed using J version 701. This chapter last updated 19 Mar 2013
Copyright © Roger Stokes 2012. This material may be freely reproduced, provided that this copyright notice is also reproduced.

>>  <<  Usr  Pri  JfC  LJ  Phr  Dic  Rel  Voc  !:  Help  Learning J