Odds And Ends

<< >> Contents Help

# 33. Odds And Ends

To keep my discussion from wandering too far afield I left out a number of useful features of J. I will discuss some of them briefly here.

Dyad # Revisited

x # y does not require that x be a Boolean list. The items of x actually tell how many copies of the corresponding item of y to include in the result:

1 2 0 2 # 5 6 7 8

5 6 6 8 8

Boolean x, used for simple selection, is a special case. If an item of x is complex, the imaginary part tells how many cells of fill to insert after making the copies of the item of y . The fill atom is the usual 0, ' ', or a: depending on the type of y, but the fit conjunction !.f may be used to specify f as the fill:

1j2 1 0j1 2 # 5 6 7 8

5 0 0 6 0 8 8

1j2 1 0j1 2 (#!.99) 5 6 7 8

5 99 99 6 99 8 8

Finally, a scalar x is replicated to the length of y . This is a good way to take all items of y if x is 1, or no items if x is 0 .

Boxed words to string: Monad ;:^:_1

;:^:_1 y converts y from a list of boxed strings to a single character string with spaces between the boxed strings.

;:^:_1 ('a';'list';'of';'words')

a list of words

Spread: #^:_1

x #^:_1 y creates an array with the items of y in the positions corresponding to nonzero items of the Boolean vector x, and fills in the other items. +/x must equal #y .

1 1 0 0 1 #^:_1 'abc'

ab c

You can specify a fill atom, but if you do you must bond x to # rather than giving it as a left operand:

1 1 0 0 1&#^:_1!.'x' 'abc'

abxxc

Choose From Lists Item-By-Item: monad m}

Suppose you have two arrays a and b and a Boolean list m, and you want to create a composite list from a and b using each item of m to select the corresponding item of either a (if the item of m is 0) or b (if 1). You could simply write

m {"_1 a ,. b

and have the answer. There's nothing wrong with that, but J has a little doodad that is faster and uses less space, as long as you want to assign the result to a name. You write

name =. m} a ,: b

(assignment with =: works too). This form does not create the intermediate result from dyad ,: . If name is the same as a or b, the whole operation is done in-place.

More than two arrays may be merged this way, using the form

name =. m} a , b , … ,: c

in which each item of m selects from one of a, b, …, c . The operation is not done in-place but it avoids forming the intermediate result.

Recursion: $:

In tacit verbs, recursion can be performed elegantly using the verb $:, which stands for the longest verb-phrase it appears in (that is, the anonymous verb, created by parsing the sentence containing the $:, whose execution resulted in executing the $:). Recursion is customarily demonstrated with the factorial function, which we can write as:

factorial =: (* factorial@<:) ^: (1&<)

factorial 4

24

factorial(n) is defined as n*factorial(n-1), except that factorial(1) is 1. Here we just wrote out the recursion by referring to factorial by name. Using $:, we can recur without a name:

(* $:@<:) ^: (1&<) 4

24

$: stands for the whole verb containing the $:, namely (* $:@<:) ^: (1&<) .

Make a Table: Adverb dyad u/

x u/ y is x u"(lu,_) y where lu is the left rank of u . Thus, each cell of x individually, and the entire y, are supplied as operands to u .

The definition is simplicity itself, and yet many J programmers stumble learning it. I think the problem comes from learning dyad u/ by the example of a multiplication table.

The key is to note that each cell of x is applied to the entire y : cell, not item or atom. The rank of a cell depends on the left rank of u . The multiplication table comes from a verb with rank 0:

1 2 3 */ 1 2 3

1 2 3

2 4 6

3 6 9

You can control the result by specifying the rank of u :

(i. 2 2) ,"1/ 8 9

0 1 8 9

2 3 8 9

(i. 2 2) ,"0 _/ 8 9

0 8 9

1 8 9

2 8 9

3 8 9

These results follow directly from the definition of dyad u/ . fndisplay shows the details:

defverbs 'comma'

(i. 2 2) comma"1/ 8 9

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

|(0 1) comma 8 9|(2 3) comma 8 9|

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

(i. 2 2) comma"0 _/ 8 9

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

|0 comma 8 9|1 comma 8 9|

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

|2 comma 8 9|3 comma 8 9|

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

Boolean Functions: Dyad m b.

Functions on Boolean operands

I will just illustrate Boolean dyad m b. by example. m b. is a verb with rank 0. m, when in the range 0-15, selects the Boolean function:

9 b./~ 0 1

1 0

0 1

u/~ 0 1 is the function table with x values running down the left and y values running along the top. 9 is 1001 binary (in J, 2b1001), and the function table of 9 b. is 1 0 0 1 if you enfile it into a vector. Similarly:

, 14 b./~ 0 1

1 1 1 0

You can use m b. in place of combinations of Boolean verbs. Unfortunately, comparison verbs like > and <: have better performance than m b., so you may have to pay a performance penalty if you write, for example, 2 b. instead of >, even though they give the same results on Booleans:

>/~ 0 1

0 0

1 0

J verb-equivalents for the cases of m b. are: 0 0"0; 1 *.; 2 >; 3 ["0; 4 <; 5 ]"0; 6 ~:; 7 +.; 8 +:; 9 =; 10 -.@]"0; 11 >:; 12 -.@["0; 13 <:; 14 *:; 15 1"0 .

Bitwise Boolean Operations on Integers

When m is in the range 16-31, dyad m b. specifies a bitwise Boolean operation in which the operation (m-16) b. is applied to corresponding bits of x and y . Since 6 b. is exclusive OR, 22 b. is bitwise exclusive OR:

5 (22 b.) 7

2

The XOR operation is performed bit-by-bit.

Dyad 32 b. is bitwise left rotate: bits shifted off the end of the word are shifted into vacated positions at the other end.

Dyad 33 b. is bitwise unsigned left shift. x is the number of bits to shift y (positive x shifts left; negative x shifts right; in both cases zeros are shifted into vacated bit positions):

2 (33 b.) 5

20

Dyad 34 b. is bitwise signed left shift: it differs from the unsigned shift only when x and y are both negative (i. e. right shift of a negative number), in which case the vacated bit positions are filled with 1).

If you use shift and rotate, you may need to know the word-size of your machine. One way to do that is

>: 2 ^. | _1 (32 b.) 1

32

Operations Inside Boxes: u L: n, u S: n

u&.> is the recommended way to perform an operation on the contents of a box, leaving the result boxed. It is the idiom used most often by J coders and the first one to be supported by special code when performance improvements are made in the interpreter.

Sometimes your operations inside boxes require greater control than u&.> can provide. For example, you may need to operate on the innermost boxes where the boxing level varies from box to box. In these cases consider using u L: n which has infinite rank. It goes inside the operands and applies u to contents at boxing level n .

The monadic case u L: n y is the simpler one. It is defined recursively. If the boxing level of y is no more than n, the result is u y . Otherwise, u L: n is applied to each opened atom of y, and the result of that is boxed. The effect is that u is applied on each level-n subbox and the result replaces that subbox, with outer levels of boxing intact. For example,

]a =. 0;(1 2;3 4 5);<<6;7 8;9

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

|0|+---+-----+|+---------+|

| ||1 2|3 4 5|||+-+---+-+||

| |+---+-----+|||6|7 8|9|||

| | ||+-+---+-+||

| | |+---------+|

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

A boxed noun.

L. a

3

Its boxing level is 3.

# L:0 a

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

|1|+-+-+|+-------+|

| ||2|3|||+-+-+-+||

| |+-+-+|||1|2|1|||

| | ||+-+-+-+||

| | |+-------+|

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

The contents of each innermost box (where boxing level is 0) is replaced by the number of items there.

# L:1 a

+-+-+---+

|1|2|+-+|

| | ||3||

| | |+-+|

+-+-+---+

Each level-1 boxed entity is replaced by the number of items.

# L:2 a

+-+-+-+

|1|2|1|

+-+-+-+

Similarly for level-2 entities.

# L:_2 a

+-+-+---+

|1|2|+-+|

| | ||3||

| | |+-+|

+-+-+---+

Negative level -n means ((level of y) minus n). Note that this does not mean 'n levels up from the bottom of each branch of y'. That would result in u's being applied at different levels in the different items of y; instead, the level at which u is to be applied is calculated using the level of the entire y .

The dyadic case x u L: n y is similar, but you need to know how the items of x and y correspond. During the recursion, as long as both x and y have a higher boxing level than the one specified in n, the atoms of x and y are matched as they would be matched in processing a verb with rank 0 0 (with replication of cells if necessary). If either operand is at the specified level, it is not changed as the items of the other operand only are opened. When both operands are at or below the specified boxing level, u is applied between them. The results of each recursion are boxed; this will give each the deeper boxing level of the two operands at each application of u .An example:

(0 1;<2;3) +L:0 (10 20)

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

|10 21|+-----+-----+|

| ||12 22|13 23||

| |+-----+-----+|

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

y was passed through and applied to each level-0 entity.

(0 1;<2;3) +L:0 (<<10 20)

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

|+-----+|+-----+-----+|

||10 21|||12 22|13 23||

|+-----+|+-----+-----+|

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

Once again y was applied to each entity, but because it has boxing level 2, all the results have boxing level 2.

The conjunction S: is like L:, but instead of preserving the boxing of the operands it accumulates all results into a list:

(0 1;<2;3) +S:0 (<<10 20)

10 21

12 22

13 23

Comparison Tolerance !.f

Like a diamond earring that adds a sparkle to any outfit, the fit conjunction !. is a general-purpose modifier whose interpretation is up to the verb it modifies. We have seen !.f used to specify the fill atom for a verb, and to alter the formatting of monad ": . Its other important use is in specifying the comparison tolerance for comparisons. A comparison like x = y calls two operands equal if they are close, where close is defined as differing by no more than the comparison tolerance times the magnitude of the larger number. If you want exact comparison, you can set the comparison tolerance to 0 using !.0 :

1 (=!.0) 1.000000000000001

0

1 = 1.000000000000001

1

Tolerant comparison is used in the obvious places—verbs like dyad =, dyad >, and dyad -:—and also in some unobvious ones, like the verbs monad ~., monad ~:, and dyad i., and the adverb /. . For all of these you can specify comparison tolerance with !.f . You may wonder whether an exact comparison using !.0 is faster than a tolerant comparison. The answer is yes, but often not by much. There is one important exception: if the comparison is used for finding equal items whose rank is greater than 0 (or are complex numbers), exact comparison can be much faster. So, if x has rank 2 or higher, it's worth the trouble to write x u/.!.0 y or x i.!.0 y; similarly use ~.!.0 y, ~:!.0 y, and x e.!.0 y if y has rank greater than 0.

i.!.0 uses a completely different algorithm from dyad i. . If performance analysis shows that dyad i. is taking a lot of time, you might get an improvement by using i.!.0, even if what you are comparing is not numeric.

The f in !.f can be no larger than about 2^_34 . The reason for this is that there is much special code in J for handling integer operands, and for speed it assumes that comparison tolerance cannot affect integer comparisons.

The foreign 9!:19 y can be used to change the default comparison tolerance, and 9!:18 '' will return the current setting.

Right Shift: Monad |.!.f

One of my personal favorites is the infinite-rank verb monad |.!.f , defined as _1&(|.!.f); in other words it shifts y right one place, discarding the last item and shifting an item of fs into the first position.

Generalized Transpose: Dyad |:

Dyad |: has rank 1 _ . x |: y rearranges y so that the axes given in x become the last axes of the result. So, if y has rank 3, 0 |: y puts the axes of y into the order 1 2 3 0 and 0 2 |: y puts them into the order 1 3 0 2 . For example:

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

0 |: i. 2 3 4

0 12

1 13

2 14

3 15

4 16

5 17

6 18

7 19

8 20

9 21

10 22

11 23

Formally, putting the axes into an order p means that (<p{x) { p |: y is the same as (<x) { y . I wish I could give you an intuitive definition but I can't.

An item of x can be negative to count axes from the end. The Dictionary shows how you can use boxed x to take elements along diagonals of y .

Monad i: and Dyad i:

Monad i: is like monad i., but its interval is centered on 0 rather than starting at 0:

i: 5

_5 _4 _3 _2 _1 0 1 2 3 4 5

i: _5

5 4 3 2 1 0 _1 _2 _3 _4 _5

Monad i: can also take a complex operand to specify a different spacing between items of the result.

Dyad i: is like dyad i., but it gives the index of the last occurrence (or #x if there is none).

Fast String Searching: s: (Symbols)

If you find your program taking a lot of time matching strings, you can create symbols representing the strings and then match the symbols rather than the strings themselves. The interpreter uses special code to make symbol-matching very fast.

Symbol is an atomic data type (like numeric, literal, and box). In a noun of the symbol type, each atom represents a boxed character string. You create a symbol with monad s: which has infinite rank. s: y takes an array of boxed strings y and creates an array of symbols of the same shape as y :

]sym =. s: 2 2$'abc';'def';'ghi';'jk'

`abc `def

`ghi `jk

$sym

2 2

The '`' characters are a clue that sym is an array of symbols. The value of the top-left atom of sym is not '`abc' or 'abc'; it is a value understood only by the interpreter. The interpreter chooses to display the text associated with the symbol, but that text is actually stored in the interpreter's private memory.

y in s: y can be a character string which is chopped into pieces using the leading character as a separator; each piece is then converted to a symbol. This is a handy way of creating a short list of symbols:

s: '`abc`ghi'

`abc `ghi

Symbols can be operands of any verb that does not perform arithmetic; in addition, comparison between symbols is allowed with 'less than' defined to mean 'earlier in alphabetical order'.

a =. s: '`abc`def`ghi`jk'

defines a list of 4 symbols.

a i. s:<'ghi'

2

We create a symbol to represent 'ghi' and find that in the list.

a i. <'ghi'

4

Note: the boxed string <'ghi' is not a symbol, so it is not found in the list.

Dyad s: has a number of forms for operating on symbols. The only one of interest to us here is 5 s: y which converts each symbol in y to its corresponding boxed string:

5 s: 3 1 { a

+--+---+

|jk|def|

+--+---+

When a string is converted to a symbol, the interpreter allocates internal resources to hold the string's value and other information. There is no way to tell the interpreter to free the resources for a single string; this can be a problem if your symbol table is large and changes dynamically. It is possible to clear the entire symbol table (using y=.0 s: 10 and 10 s: y), but doing so invalidates any symbols previously created by s: y .

If you would like to do high-speed matching but what you want to match is not a string, consider converting to strings using 5!:5 <'y' which converts the variable named y to string form.

Unicode Characters: u:

2-byte unicode characters can be represented by variables that have the unicode atomic data type. Such variables are created by the verb u: . Its use is described in the Dictionary.

Window Driver And Form Editor

Designing user interfaces is quick and painless with J's Form Editor. The Lab named Form Editor will show you how.

<< >> Contents Help