Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Rich H.J for C programmers.2006.pdf
Скачиваний:
18
Добавлен:
23.08.2013
Размер:
1.79 Mб
Скачать

6. Loopless Code I—Verbs Have Rank

Most J programs contain no loops equivalent to while and for in C. J does contain while. and for. constructs, but they carry a performance penalty and are a wise choice only when the body of the loop is a time-consuming operation. You are just going to have to learn to learn to code without loops.

I think this is the most intimidating thing about learning J—more intimidating even than programs that look like a three-year-old with a particular fondness for periods and colons was set before the keyboard. You have developed a solid understanding of loops, and can hardly think of programming without using them. But J is a revolutionary language, and all that is solid melts into air: you will find that most of your loops disappear altogether, and the rest are replaced by small gestures to the interpreter indicating your intentions.

Come, let us see how it can be done. I promise, if you code in J for 6 months, you will no longer think in loops, and if you stay with it for 2 years, you will see that looping code was an artifact of early programming languages, ready to be displayed in museums along with vacuum tubes, delay lines, and punched cards. Remember, in the 1960s programmers laughed at the idea of programming without gotos!

You are not used to classifying loops according to their function, but I am going to do so as a way of introducting J's primitives. We will treat the subject of loopless iteration in 6 scattered chapters, showing how to replace different variants of loops:

1.Loops where each iteration of the loop performs the same operation on different data;

2.Loops that apply an operation between all the items of the array, for example finding the largest item;

3.Loops where the operation to be performed on each cell is different;

4.Loops that are applied to regularly-defined subsets of the data;

5.Loops that are applied to subsets of the data defined irregularly;

6.Loops that accumulate information between iterations of the loop;

7.Loops that implement finite-state machines.

The simplest case is the most important, and we start with a few experiments.

Examples of Implicit Loops

2 + 3 4 5

5 6 7

The verb dyad + is addition, and we have our first example of an implicit loop: the left argument 2 was added to each atom in the right argument.

32

5

7

1 2 3 + 4 5 6

9

And look! If each operand is a list, the respective items are added. We wonder if the behavior of 2 + 3 4 5 was because items of the shorter operand are repeated cyclically:

1 2 + 4 5 6

 

|length

error

5 6

| 1 2

+4

Evidently not. A 'length error' means that the operands to + did not 'agree' (and you get an error if you try to add them). We will shortly understand exactly what this means.

0

1

i. 2 3

2

3

4

5

A reminder of what monad i. does.

0

0 100 + i. 2 3

1

2

103

104

105

Whoa! The atoms of the left operand were applied to rows of the right operand. Interesting. This seems to be some kind of nested implicit loop.

Let's learn a couple of more verbs, monad #. and monad #: . Monad #: creates the binary representation of an integer (i. e. a list of 0s and 1s), and monad #. is its inverse, creating the integer from the binary representation. For the longest time I couldn't remember which was which, but at last I saw the mnemonic: the verb with the single dot (#.) creates an atom from a list; the verb with multiple dots (#:) creates a list from an atom:

#: 5 1 0 1

#. 1 0 1

5

Yes, they seem to perform as advertised. They can be applied to arrays:

]a =. #: 5 9 0 1 0 1 1 0 0 1

Look: the result is not a rank-1 list, but rather a rank-2 array, where each item has the binary representation of one operand value (and notice, an extra leading zero was added to the representation of 5). The little trick with ]a =. will be explained later, but for now just think of ]a =. as 'assign to a and display the result'. With a assigned, we have:

#. a

5 9

This seems to be the desired result, but on reflection we are puzzled: how did the interpreter know to apply #. to each 1-cell rather than to each 0-cell? Contrast this result with the result of the verb monad +:, which means 'multiply by 2':

33

+: a 0 2 0 2 2 0 0 2

Evidently the verbs themselves have some attribute that affects the rank of cell they are applied to. It's time for us to stop experimenting and learn what that attribute is.

The Concept of Verb Rank

Every verb has a rank—the rank of the cells to which it is applied. If the rank of the verb's operand is smaller than the rank of the verb, the verb is applied to the entire operand and it is up to the author of the verb to ensure that it produces a meaningful result in that case.

Dyads have a rank for each operand, not necessarily the same.

A verb's rank can be infinite (_), in which case the verb is always applied to the operand in its entirety. In other words, if a verb has infinite rank for an operand, that operand is always processed as a single cell (having the rank of the operand).

If you don't know the rank of a verb, you don't know the verb. Using a verb of unknown rank is like wiring in a power-supply of unknown voltage—it will do something when you plug it in; it might even work; but if the voltage is wrong it will destroy what it's connected to. Avoid embarrassment! Know the rank of the verbs you use.

The definition page of each J verb gives the ranks of the verbs defined on the page, right at the top of the page after the name of the verb. Since most pages define both a monad and a dyad, you will usually find 3 numbers: the first is the rank of the monad, the other two are the left and right rank of the dyad. For example, click up the page for #: and you will see

#: _ 1 0

which means that monad #: has infinite rank, while dyad #: has left rank 1 and right rank 0. For any verb, including user-written verbs, you can ask the interpreter the rank by typing verbname b. 0 :

#: b. 0 _ 1 0

Verb Execution—How Rank Is Used (Monads)

The implicit looping in J results from the interplay of verb rank and noun rank. For monads, it goes like this:

1.Figure out the rank r of the cells that will be operated on; this will be the smaller of rank of the verb and the rank of the operand. This rule applies even if the verb has infinite rank: r will be the rank of the operand, which is another way of saying that the verb applies to the operand in its entirety.

2.Find the frame f of the operand with respect to cells of rank r.

3.Think of the operand as an array with shape f whose items are cells of rank r. Apply the verb to each r-cell, replacing each cell with the result of the verb.

34

Obviously, this will yield an array of shape f whose items have the shape of the result of applying the verb to an r-cell.

Let's look at some simple examples:

i. 2 2

0 1

2 3

This will be the right operand.

+: i. 2 2

0 2

4 6

The steps to get this result are:

The verb rank is 0 and the noun rank is 2, so we will be applying the verb to 0-cells. The frame f is 2 2

 

 

 

 

 

 

 

 

 

 

 

 

Think of the operand as a 2×2 array of

 

0

1

 

 

 

 

 

0-cells:

 

2

3

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

The verb is applied to each cell:

 

0

2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

4

6

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Since each result is an atom, i. e. a 0-cell, the

 

 

0 2

 

 

 

result is a 2×2 array of 0-cells, i. e. an array of

 

 

4 6

 

 

 

 

shape 2 2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Figure 1. Execution of +: i. 2 2

 

 

 

 

Another example:

$ 0 0 1 1 0 0 0 1 0 1 0 0 0 0 1 0

0

0

]a =. 2 2 4

1 1

 

 

 

 

 

0

0

0 1

 

 

 

 

 

0 1 0 0

0 0 1 0

This is a rank-3 array.

35