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

41. Readable Tacit Definitions

Heron's formula for the area of a triangle, given the lengths of the sides a, b, and c, is sqrt(s*(s-a)*(s-b)*s-c)) where s is (a+b+c)%2 . This can be written

triarea =: [: %: [: */ -:@:(+/) - 0 , ] triarea 3 4 5

6

Regardless of your diligence in commenting your code and the level of J expertise in you and the sorry wretches who will have to read it, long tacit definitions like this can become trackless wastelands, Saharas of verb following verb unendingly with nothing to suggest an interpretation of the intermediate results. I know of two ways to mark a trail through such a definition. The first is to develop a regimen for using spaces and parentheses to

help the reader group the parts. I would write

0,]

triarea =: [: %: [: */ (-:@:(+/)) -

The second way is to split the line into multiple lines, where each line can have a comment or a verb-name indicating what it produces. This approach, carried almost to an extreme, would yield

semiperimeter =: -:@:(+/) factors =: semiperimeter - 0,] product =: [: */ factors triarea =: [: %: product

Combining the two approaches, you can find a comfortable level of complexity.

Flatten a Verb: Adverb f.

Splitting the definition into many lines has the unfortunate side-effect that all the names referred to by triarea must be defined when triarea is executed:

triarea [: %: product

triarea refers to product which refers to factors which refers to semiperimeter . If you define many tacit verbs this way, the result is pollution of the namespace. To leave a smaller footprint, use private assignment for all the names except the name that will be public, and use the adverb f. which replaces names in its operand with their definitions:

semiperimeter =. -:@:(+/) factors =. semiperimeter - 0,] product =. [: */ factors triarea =: ([: %: product) f. triarea

[: %: [: */ -:@:(+/) - 0 , ]

If these verbs are run from a script, the temporary verbs will disappear (since they were assigned by private assignment), leaving only the main verb triarea .

f. is also used in initialization of objects, as you can learn in the Lab for ObjectOriented Programming.

236

Note that f. has no effect on explicit definitions.

Using f. to improve performance

Flattening a verb has two beneficial effects on performance. The first is easy to see by comparing two equivalent sentences:

a =. i. 100000

abs0 =: 3 : '| y.' "0 6!:2 'abs0 a'

3.17136

abs1 =: 3 : '| y.' 6!:2 'abs1"0 a'

3.54908

To be sure, in each case we have committed the crime of applying an explicitly-defined verb at a low rank (|"0 a executes in time 0.006), but that is not the point. Why is abs1"0 slower than abs0? Each one reinterprets its verb for each atom of a .

The answer is that when abs1"0 is executed, the definition of abs1 must be looked up for every atom of a (for all the interpreter knows, abs1 might be redefined during its execution). The time spent doing this lookup accounts for the difference in time between abs0 and abs1"0 . If we eliminate the lookup of the name abs1, that time is saved:

6!:2 'abs1 f."0 a' 3.00941

The important lesson to learn from this is that you should define your verbs with the proper rank. That will eliminate superfluous name lookups. In exceptional cases you may use f. to avoid name lookups during execution of a complex verb.

The second case where f. can improve performance is useful only for those users who feel compelled to redefine the J primitives with mnemonic names. This is a practice that I strongly deprecate, and if you don't heed my advice, the interpreter stands ready to punish you. See the disaster that can result when the primitives are replaced by mnemonic names:

tally =: #

a =. 100000 $ i. 6 b =. i. 100000 10 6!:2 'a #/. b'

0.0157688

6!:2 'a tally/. b' 0.154675

What happened is that #/. is handled by special code in the interpreter, but tally/. is not. The fact that tally is defined to be # is immaterial: the interpreter doesn't know that at the time it creates the anonymous verb for tally/. . The penalty is an almosttenfold increase in execution time.

6!:2 'a tally f./. b' 0.0167351

By flattening tally, we cause it to be replaced by its definition #, and then the special case #/. is recognized.

237

b =. 0 = i. 100000 6!:2 '(# i.@#) b'

0.0011616

6!:2 '(tally index@tally) b' 0.00385943

Another example: (# i.@#) is handled by special code, but the names prevent the interpreter from recognizing the situation.

6!:2 '(tally index@tally) f. b' 0.0011861

If we flatten every verb, we get good performance, but what an effort! It's much better to use the J primitives directly, so the interpreter can do its job effectively.

238

42.Explicit-To-Tacit Converter

Q:What's the most common cause of blindness among J programmers?

A:Conjunctivitis.

In the early weeks, complex tacit definitions are torturous to write and next-to- impossible to read; blurred vision and sleepless nights are the occupational hazard of the programmer who dives into tacit J. If a co-worker is banging into doors as he stumbles to refill his tankard of coffee, here's how to check him out: hold up three fingers and ask him how many he sees. If he says "three", he's merely fallen in love or is working to a deadline, and he will recover. But if he replies "I see a list of 3 items each of which is a finger", you can expect to start receiving emails that look like they've been encrypted.

You can offer as a temporary palliative J's mechanism for converting an explicit definition to a tacit one. You request the conversion by using a left operand to : in the

range 11 to 13

instead of 1 to 4 .

9!:3 (5)

NB. Do this once to select simplified display

3 : 'x. - y.'

3 : 'x.-y.'

We defined a verb, and since we didn't assign it to anything, we see its value, which is just what we defined it to be.

13 : 'x. - y.'

-

by using 13 instead of 3, we ask the interpreter to try to find a tacit equivalent, which it did.

Here is another way to define the verb enclose from the previous chapter:

13 : '(>{.x.) , y. , (>{:x.)' ([: > [: {. [) , ] , [: > [: {: [

The interpreter likes to use [: in its tacit definitions. Note that you use 13 : to get an equivalent for both monadic and dyadic verbs; there is no 14 : .

I recommend that you use 13 : as your first choice for defining tacit verbs. It will find all tacit equivalents that can be generated systematically, and the explicit definition is much easier to read than a tacit definition. You will still have to use your tacitdefinition skills for irregular cases, such as

13 : '+:^:x. y.'

4 : '+:^:x. y.'

If x. or y. is used as an operand to a conjunction, as in this example, the tacit converter is not going to be able to make a tacit equivalent. The result of 13 : is still a verb performing the requested function, but it is an explicit definition rather than a tacit one. Note that the interpreter saw that the definition contained a reference to x., so it made the verb a dyad.

239