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

12. Compound Verbs

On New Year's Day my rich uncle gives me x dollars, which I add to the y dollars I already have earning 4% interest. How much money will I have at the end of the year? Simple in J—I just write 1.04 * x + y, and I have the answer, whether x and y are scalars or arrays. That's nice, but I have an enviable problem: I expect his largesse to continue, and in my anticipation I have estimated his gifts for the next few years as the list x; I want to know what I'll be left with at the end. I need to pass each year's starting balance into the calculation for the next year. I know what I want the result to look like: it'll be v/ (|.x) , y which will be evaluated as xn v …x2 v x1 v x0 v y . But what is v? The problem with 1.04 * x + y is that it contains 2 verbs and a constant, and I need it all lumped into a single verb so that I can have the adverb dyad / modify the whole thing. One solution would be to create the verb

v =: dyad : '1.04 * x. + y.'

after which v/ (|.x),y works, but it's a shame to have to interrupt a J sentence just to define a verb with such a puny function—I want magic words to let me say

(1.04 * + abracadabra…combine!)/ (|.x),y . J has such magic words, and we will learn a few now.

The magic words will join verbs and nouns together, so they must be modifiers: adverbs and conjunctions. Before we start, we need a little notation to help with the different cases we will encounter. Given a conjunction c or adverb a, we call its left operand m if it is a noun, or u if it is a verb. Similarly we call a conjunction's right operand n if it is noun, v if a verb. There are four possible ways to invoke a conjunction (u c v, m c v, u c n, and m c n) and two for an adverb (u a and m a) and they are defined independently. Moreover, the derived verb produced by the invocation (the derived entity may be a noun, adverb, or conjunction too but that is unusual) can be used as a dyad (e. g. x u c n y) or as a monad (e. g. m a y), and those cases are defined independently as well. You won't get the cases mixed up, because verbs and nouns are so different that it will seem natural for u c n to be different from u c v; just be aware that the variants are many and that we will be learning a tiny subset of J's toolkit. The adverb / is an example: we have learned about monad u/, but dyad u/ is very different, as is m/ .

Verb Sequences—u@:v and u@v

u@:v creates a derived verb of infinite rank that applies v to its argument(s) and then applies u to the result. In other words, u@:v y is the same as u v y and x u@:v y is the same as u x v y . Examples:

{. @: /: 3 1 4 1 5 9

1

Monad /: produced the permutation 1 3 0 2 4 5 of which we took the first item.

80

1 2 3 +/@:* 1 2 3

14

Dyad * produced 1 4 9 whose items we then summed. fndisplay shows the details:

 

defverbs 'plus"0 times"0'

+

1 2 3 plus/@:times 1 2 3

-------------------------------------------+

|(1 times 1) plus (2 times 2) plus 3 times 3| +-------------------------------------------+

u@v is like u@:v except that the rank of the derived verb is the rank of v (also expressible as (u@:v)"v because u"v is defined to have the function of u with the rank of v). My advice is to stick to @: and avoid @ unless you're sure you need it.

The Difference Between u@:v and u@v

Because u@:v and u@v have very similar definitions, and produce identical results in many cases, almost every beginning J programmer confounds the two. The key is to remember that each sequence produces a new verb which has a rank. In u@:v, this rank is infinite, so that in x u@:v y, the derived verb u@:v is applied to the entire x and y, meaning that v is applied to the entire x and y and u is applied to the entire result of v . In the other case, the rank of u@v is the rank of v, so in x u@v y the verb u@v is applied to individual cells of x and y, where the cell-size is given by the rank of v : for each of those cells, v is applied followed by u, and the results from the cells are collected into an array.

If we try to take the sum-of-products using u@v instead of u@:v, we see the difference between the two forms:

1 2 3 +/@* 1 2 3

1 4 9

What happened? We thought we were multiplying the vectors and then taking the sum. Because we used @ rather than @:, the derived verb had the rank of dyad *, namely 0, which means that the derived verb was applied to each cell: at each cell we multiplied and then took the sum of the single cell. In fndisplay form,

 

defverbs 'plus"0 times"0'

+

1 2 3

plus/@times 1

2 3

---------

+---------

+---------

+

|1 times

1|2 times 2|3

times 3|

+---------

 

+---------

+---------

+

plus never got executed, because plus/ was applied to 1-element lists, leaving in each case the single element.

Many J programmers think of @ and @: as establishing a different kind of connection between u and v, with u@:v applying u to the entire result of v and u@v applying u to result cells of v (where a result cell is the output produced by applying v to a single operand cell). Such an interpretation makes it easy to understand the operation of

+/@* : +/ is applied on result cells of *, which are scalars.

81

The connection interpretation of u@v correctly accounts for the results produced by J, but as you use it you should be aware that it is inaccurate because it suggests that v is executed against the operand(s) in their entirety. The actual cell-at-a-time execution of u@v is different in two ways: it is slower because the verb v must be restarted for each cell; and if the temporary space required by u or v is large, cell-at-a-time execution uses less space because the temporary space for each cell is freed before the next cell is processed.

An Exercise in @ and @:

Given the definition

]a =: 1 2 3 ; 4 5 ; 6 7 8 +-----+---+-----+ |1 2 3|4 5|6 7 8| +-----+---+-----+

we want to create a verb that will throw away the first box (using the Behead verb, monad }.), and for each remaining box open it and keep just the first atom (using the Head verb, monad {.). The result will be a list of the first items in all boxes except the first, in this case 4 6 . Understand how the following verbs work or don't work:

4

6

}.@:({.@>) a

NB. Verb 1

}.@({.@>) a

NB. Verb 2 (the output is 3 blank lines)

 

 

 

 

}.@:{.@> a NB. Verb 3 (the output is 3 blank lines)

2

3

}.@({.@:>) a

NB. Verb 4

{.@>@}. a

NB. Verb 5

4

6

{.@:>@}. a

NB. Verb 6

4

5

0

 

Solutions:

In verb 1 (}.@:({.@>)), the overall verb has infinite rank because of the use of @:, so the entire a is applied to ({.@>) and then }. operates on the entire result. {.@> has rank 0 because @ was used and the rank of > is 0. So each atom of a, i. e. each box, is separately opened and the first element taken, giving a 3-element vector 1 4 6 which is then beheaded to give 4 6 . The following table shows how the operand is passed through the verb, with each row representing a cell of input to each verb. The table should be read from right to left.

82

Result

Processing of {.@>

 

 

 

 

 

 

 

 

of }.

 

 

 

 

 

 

 

 

 

 

 

 

Result of

Result

Result

 

Cells of

 

 

 

 

 

(final

 

 

Cells of }.@:({.@>)

result)

{.@>

of {.

of >

 

{.@>

 

 

 

 

1

1 2 3

 

 

 

 

 

 

 

 

 

 

 

 

1 2 3

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

4

4 5

 

 

 

 

 

 

 

 

 

4 6

1 4 6

 

4 5

 

 

 

1 2 3

4 5

6 7 8

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

6

6 7 8

 

 

 

 

 

 

 

 

 

 

 

 

6 7 8

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

In verb 2 (}.@({.@>)), the overall verb has rank 0 because @ was used and the rank of ({.@>) is 0. So each atom of a, i. e. each box, will be opened, the first element taken, and then the first element of that beheaded, leaving an empty list; this is done for each atom of a, giving a result of 3 0$0 which displays 3 blank lines.

 

 

Processing of {.@>

 

 

 

 

 

 

 

Final

Result

Result of

Result

Result

 

Cells of

 

Cells of }.@({.@>)

result

of }.

{.@>

of {.

of >

 

{.@>

 

 

 

 

 

 

1 2 3

 

 

 

 

 

 

 

 

 

0$0

1

1

 

1 2 3

 

 

1 2 3

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3$0

 

 

 

4 5

 

 

 

 

 

 

 

 

0$0

4

4

 

4 5

 

 

 

4 5

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

6 7 8

 

 

 

 

 

 

 

 

 

0$0

6

6

 

6 7 8

 

 

6 7 8

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

In verb 3 (}.@:{.@>), the ordering rules for modifiers cause the verb to be equivalent to (}.@:{.)@>, which again has rank 0 because of @ with right operand of >, so each atom is passed through the whole verb giving the same result as verb 2.

 

Processing of }.@:{.

 

 

 

 

 

Final

Result of

Result

Result

Result of

 

Cells of }.@:{.@>

result

}.@:{.

of }.

of {.

>

 

 

 

 

 

 

 

 

 

 

 

0$0

0$0

1

1 2 3

 

1 2 3

 

3$0

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

0$0

0$0

4

4 5

 

4 5

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

0$0

0$0

6

6 7 8

 

6 7 8

 

 

 

 

 

 

 

 

 

 

In verb 4 (}.@({.@:>)), the rank of the overall verb is infinite because @: makes the rank of ({.@:>) infinite. The entire a is passed into ({.@:>), where it is opened and padded with fills to make an array of shape 3 3; then the first item is taken, yielding 1 2 3; then this list is beheaded, giving 2 3 .

83