Help / JforC / Declarations
>> << Pri JfC LJ Phr Dic Voc !: Rel NuVoc wd Help J for C Programmers
5. Declarations
J has no declarations. Good riddance! No more will you have to warn the computer of all the names you intend to use, and their types and sizes. No more will your program crash if you step outside an array bound. You specify the calculations you want to perform; if, along the way, you want to assign a result to a name, J will allocate enough space for the data. It will free the space when the name is no longer needed.
Seasoned C programmers have learned to use declarations to create a web of type-checking, making sure that objects pointed to are of the expected type. This is an example of making a virtue of necessity. Since J avoids pointer trouble much more directly--by not having pointers at all--you will soon lose your uneasiness with weak typing.
Arrays
But, you ask, Without declarations, how does the computer know that a name represents an array? For that matter, how do I know that a name represents an array?
The answer affords a first glimpse of the power of J. Every J verb, whether a primitive (operator) or a user-written verb (function), accepts arguments that can be arrays, even multidimensional arrays. How is this possible? Like this: Suppose you write a verb that works with 2-dimensional arrays. Part of your verb definition will indicate that fact. If your verb is executed with an argument that is a 3-dimensional array, J will automatically split the 3-dimensional array into a sequence of 2-dimensional arrays, call your verb for each one, and put the results together into an array of results.
We will very soon go into this procedure in great detail. For now, you should learn the vocabulary J uses to deal with arrays.
What C calls an n-dimensional array of rank ixjx...xk is in J an array of rank n with axes of length i,j,...,k.
Every noun (variable or object) has a shape which is the array (of rank 1) made by concatenating the lengths of all its axes. For example, if q is the array corresponding to the C array defined by the declaration
int q[4][5][6];
its shape is the array 4 5 6 . As you can see, the number of items in the shape is exactly the rank.
Note: a sequence of numbers written with no intervening punctuation defines a numeric array of rank 1 (i. e. a list). You may have to use parentheses if you have adjacent numbers that you don't want to have made into a list.
Unlike in C, an array in J may have one or more axes of length 0. Such an array has no atoms, but its rank is still the number of its axes.
A single number or character is called an atom (object of basic type) which is said to have the type numeric or character as appropriate. (Actually, there are types other than number and character, including a type that resembles a structure and can contain components of different types, but we won't get to them for a while). An atom is also called a scalar. An atom is defined to have rank 0; therefore, its shape is an array with 0 items, i. e. an empty array of rank 1.
An atom is a degenerate array, with no axes, but it follows the rules for arrays nonetheless.
Just as in C, every atom of an array must have the same type.
Cells
Because the execution of every J verb involves breaking the argument into pieces, presenting the pieces to the verb, and assembling results, J has a vocabulary for describing these operations.
A rank-3 array of shape 4 5 6 such as the one defined in C by the declaration
int q[4][5][6];
can be thought of as an array of 4 elements, each with rank 2 and shape 5 6, or as a 4x5 array of elements, each with rank 1 and shape 6, or as a 4x5x6 array of rank-0 atoms. These smaller arrays, which can be arrayed together to constitute the larger array, are called cells. As seen in the example above, the axes of a cell are a suffix of the axes of the entire array. A cell of rank k is called a k-cell: a 0-cell is an atom, a 1-cell is an element of rank 1, a 2-cell is an element of rank 2, and so on. You can view an array as an array of its cells, and in more than one way, depending on what cell-size you choose.
Each time you operate on an array, you decide the rank of the cells to be operated on. The rank of the cells is not a property of the array; rather, it indicates how you want to group the atoms of the array for a single operation. When you have decided to operate on cells of rank n, you can think of your noun as an array of n-cells; the shape of that array of cells is called the frame of the noun relative to n-cells. It follows that the frame, concatenated with the shape of the cells, will be equal to the shape of the noun. The frame itself (like all shapes) is an array of rank 1.
The diagram illustrates cells of different ranks. Note that the twenty 6-atom 1-cells are arranged in a 4x5 array; this is the meaning of the frame of the 1-cells. The four 5x6 2-cells are arranged as a vector of 2-cells; this is the meaning of their frame.
A selected cell is analogous to the subarray selected by indexing in C. Using q as defined above, in C q[3] is a 5x6 array (i. e. a 2-cell); q[1][0] is a 6-element vector (i. e. a 1-cell); q[2][0][3] is a scalar (0-cell).
The noun q we have been using as an example can be thought of in any of the following 4 ways:
|
Frame |
Cells |
||
as an array of |
Length |
Value |
Rank |
Shape |
0-cells |
3 |
4 5 6 |
0 |
(empty) |
1-cells |
2 |
4 5 |
1 |
6 |
2-cells |
1 |
4 |
2 |
5 6 |
3-cells |
0 |
(empty) |
3 |
4 5 6 |
|
Choosing Axis Order
Because J verbs operate on cells of nouns, you should choose an order of axes that makes the cells meaningful for your application. Referring to the figure, we can see that the groups of items that fall into horizontal strips (1-cells) or horizontal slabs (2-cells) will be easy to operate on individually. Vertical strips or slabs will not correspond to cells and so will not be accessible individually; to work on them we may have to reorder the axes of the noun to make them correspond to cells. Such reordering of axes is easy in J but it can often be avoided by ordering the axes properly in the first place.
Negative Cell-Rank; Items
In some cases, you may know the length of the frame and want to define the cells to have whatever rank is left over (for example, you may have a noun with one cell per employee, but you may not know the rank of the cells). We say that you are selecting the cells of the noun relative to the given frame. Negative cell-ranks indicate this. A _1-cell has the shape corresponding to a frame of length 1 (5 6 in our example), a _2-cell has the shape corresponding to a frame of length 2 (6 in our example), and so on. If the specified frame is longer than the rank of the noun, the entire shape of the noun is used for the frame (and the cells are 0-cells, i. e. atoms). In our example, a _3-cell, a _4-cell, a _5-cell, etc., all refer to atoms. As an important case of this, the _1-cell of an atom is the atom itself.
_1-cells are so important in J that they are given the name items. Our example has 4 items, each of shape 5 6 . An atom has one item, itself.
Remember: an atom has one item: itself.
The index of an item in an array is the sequence number of the item from the beginning of the array. The first item in an array has the index 0, just as in C.
Lists
When we choose to view a non-atomic <st2:Sn w:st="on">array</st2:Sn> (i. e. an array of rank 1 or higher) as a collection of its items, we say that the array is a list of its items. In our example above, the items of the 4x5x6 array are 5x6 arrays, and we say that the whole array is a list of 4 items each with shape 5 6 (equivalently, we say that it is a list of four 5x6 arrays). So many of J's primitives operate on items of their operands that we will find ourselves usually thinking of an array as a list of its items.
When the word 'list' is used without any indication of what the list contains, the list is assumed to contain atoms. So, 'the list x' refers to an array of rank 1 (one-dimensional array). 0 3 5 is a numeric list.
Note that J's use of the term 'list' has nothing to do with linked lists such as you are familiar with, where an element in the list contains a pointer to other elements. Since J has no pointers at all, you will not need that meaning, and you can get used to calling rank-1 arrays 'lists'. A list can also be called a vector.
Phrases To Memorize
A non-atomic array is a list of its items.
An atom has one item, itself.
The rank of a noun is the length of its shape.
The shape of an atom is the empty list.
The suffixes of the shape of a noun give the shapes of its cells: the k trailing atoms of the shape of a noun give the shape of its k-cell.
The frame of a noun with respect to k-cells is the shape of the noun, with the last k atoms of the shape removed.
The terminology of nouns is summarized in the following table.
Summary of Terminology for Nouns in J |
||||
|
Atom |
Non-Atomic Array |
||
Rank |
0 |
1 |
2 |
higher n |
Name |
Atom |
List |
Table |
Rank-n Array |
Shape (always a list) |
(empty list) |
1-item list ,l |
2-item list r,c for an rxc table with r rows and c columns |
n-item list |
Items |
The atom itself |
atoms |
lists (each with shape ,c) |
(n-1)-cells |
Name/Shape if |
impossible |
empty list |
empty table |
empty array |
,0 |
0,c |
0,... |
|
Constant Lists
A character or numeric list can be created simply by including the list in a sentence. We have seen that a sequence of numbers separated by spaces is recognized as a single word representing the list. Similarly, a character or a character list can be represented directly by a quoted string. C distinguishes between single-character constants (such as 'a') and strings (such as "abc"), using single quotes for characters and double quotes for strings. J uses only single quotes for defining character constants (the " character is a primitive in its own right). If exactly one character is between the quotes, the value is an atom; if none or more than one, the result is a list.
Array-creating Verbs
Now that we know how to talk about arrays, we might as well create a few and see what they look like. As mentioned earlier, every J verb can be used to create an array--there are no special 'declaration' verbs--but we will start with a couple that do little else. The J lines are taken from an interpreter session; you can type them into your own session and get the same results. The indented lines were typed into J, and the unindented ones are J's responses.
Dyad $ ($hape) and monad $ ($hape Of)
The verb dyad $ is invoked as x $ y . The result of dyad $ has the frame x relative to the rank of the items of y, and is made up of the items of y, repeated cyclically as needed. It follows that the shape of this result is x concatenated with the shape of an item of y .
We will have to work together on this. Confronted with a definition like that, you might: (a) decide that J must be a language for tax accountants, and give up; (b) decide the definition is Greek and go on, hoping it will make sense later; (c) try a few examples to get an idea for what the definition means; (d) read it over and over again until you understand it. I hope you will eschew (a) and (b), and settle for no less than full understanding. For my part, I will offer a few useful examples that you can compare against the definition. Not everything in J will be as abstract as this.
5 $ 2 2 2 2 2 2
The simplest case, creating (and displaying) a list of 5 items, each of which is 2 . Let's see how this result matches the definition. y is a scalar, so it has one item, which is also a scalar. Therefore, the result has the shape 5 (x (i. e. 5) concatenated with the shape of an item of y; the shape of a scalar item of y is the empty list; 5 concatenated with an empty list is a list with the single element 5). The scalar is repeated to fill the 5 items of the result. J displays a 1-cell on a single line, as shown.
5 $ 'ab' ababa
Now y is a list, but its items are still scalars, with rank 0 and shape empty; so the result still has the shape 5 . The 5 items come from the items of y, cyclically.
We can distill the foregoing analysis above to the observation that when y is an atom or a list, x specifies the shape of x $ y .
4 4 $ 'There is a tide in the affairs of men' Ther e is a t ide
The items of y are still scalars, with rank 0 and shape empty; the result has the shape 4 4 . The 16 items come from the items of y, cyclically. Not all items of y are used. J displays a rank-2 array as a sequence of lines, one for each 1-cell.
0 $ 2
(The display is a single blank line) Just like 5 $ 2, but the resulting list has 0 items, i. e. it is an empty list.
1 $ 2 2
Similarly, a 1-item list.
(0 $ 2) $ 2 2
Here (0 $ 2) produces an empty list, as we saw above, and that is the x to the second $ . The items of y are still scalars, so the result has shape empty (an empty list concatenated with an empty list), i. e. it is a scalar.
The displays of a scalar and a 1-item list are identical. Does that mean that a scalar is the same thing as a 1-item list? No. I mean no. NO! They are not (I say this with the same resignation as when I tell my kids not to rollerblade too fast down our street, knowing that only painful experience will drive the message home). How can you tell them apart? What we need is a way to see the shape of a noun.
That way is monad $ . The result of $ y is the shape of y (always a numeric list). For example:
$ 1 2 3 4 4
A 4-item list has the shape 4 . Did you forget that 1 2 3 4 is a single list rather than 4 separate numbers? You can ask the interpreter how it splits a line into words by using monad ;: :
;: '$ 1 2 3 4' +-+-------+ |$|1 2 3 4| +-+-------+
The words are shown in boxes. The list 1 2 3 4 is recognized as a single word.
$ 6
The shape of a scalar is a 0-length list, as we have seen.
$ 1 $ 2 1
Remember, all sentences are executed right-to-left, so this is $ (1 $ 2), which gives the shape of the 1-item list. When a verb can be invoked dyadically, it is, so the rightmost $ is executed as a dyad, not as a monad.
$ (0 $ 2) $ 2
Here, we get the shape of the scalar--an empty list.
$ 'a'
A single character is an atom, whose shape is the empty list.
$'abc' 3
A 3-item list, one item for each character.
$ '' 0
is the empty character string, which you will see a lot of. Because it is easy to type, it is the value most often used when an empty list of any type will do.
Executing monad $ twice gives the rank: $ $ y is the rank of y (as a single-item list). I suggest you not read on until you understand why.
Resuming our inquiries into dyad $, we have
2 5 $ 1 10 1 10 1 10 1 10 1 10 1 10
Again y is a list, so the items of y are scalars. The shape of the result is 2 5, and the items of y are repeated to fill that shape. Note that the corresponding atoms in each cell are aligned in the display.
1 5 $ 1 10 1 10 1 10 1
Similarly, but now the result has shape 1 5. This is not the same as a 5-item list, which has shape 5 . Again, monad $ shows the shape:
$ 1 5 $ 1 10 1 5
When y is not a scalar or a list, its items are not scalars, and x does not give the shape of the result. Let us work through an example using the definition of x $ y :
3 $ 1 5 $ 1 10 1 10 1 10 1 1 10 1 10 1 1 10 1 10 1
Remember, this is processed as 3 $ (1 5 $ 1 10). The parenthesized part produces an array of shape 1 5; since this has rank 2, its items are its 1-cells, each with shape 5. The shape of the overall result is x concatenated with the shape of an item of y, to wit 3 5. This is populated with the cells of y, of which there is only 1.
3 $ 2 5 $ 'There is a tide in the affairs of men' There is a There
You should be able to explain where each line came from, and you should note that in general, x specifies the frame of x $ y with respect to items of y . When y is a list or an atom, its items are atoms and x gives the entire shape of the result.
2 2 $ 2 5 $ 1 10 1 10 1 10 1 10 1 10 1 10 1 10 1 10 1 10 1 10 1 10 $ 2 2 $ 2 5 $ 1 10 2 2 5
Now the shape of the result is 2 2 5, a rank-3 array. J displays the 2-cells with a blank line in between. Similarly, a rank-4 array is displayed as all the 3-cells with 2 blank lines in between, and so on for higher ranks.
We have seen that the display of a zero-length list is a single blank line: proper, as there is one list, and it has no items. The display of a rank-2 array with no items is different: here we have zero lists, so we should expect no lines at all. This is indeed what happens:
0 0$0
(there is no blank line). This is the result you should produce if you want a function to display nothing.
Here are two exercises. Once you can explain each result, you will be well on your way to becoming a J programmer. What will each of these sentences produce (answer on the next page)?
3 1 $ 2 5 $ 1 10 2 3 $ 2 5 $ 1 10 15
Solutions:
3 1 $ 2 5 $ 1 10 1 10 1 10 1 10 1 10 1 10 1 10 1 10 1 2 3 $ 2 5 $ 1 10 15 1 10 15 1 10 15 1 10 15 1 1 10 15 1 10 15 1 10 15 1 1 10 15 1 10 15 1 10 15 1
Monad # (Tally)
The result of # y is a scalar, the number of items in y . This is the first item in the list $y, except that if y is an atom, $y is empty but #y is 1 (because, remember, an atom has one item, itself). If y is a list, #y is the length of the list. Quiz question: What is the difference between the results of $ 1 2 3 and # 1 2 3?
$ 1 2 3 3 # 1 2 3 3
Answer: the result of monad $ is always a list, but the result of monad # is a scalar:
$ $ 1 2 3 1 $ # 1 2 3
#$y, like $$y, gives the rank of y . Since monad # produces a scalar rather than a list, #$y is usually preferred. Just remember that the length of the shape is the rank.
Monad i. (Integers)
Monad i. accepts a scalar or vector operand and creates an array. i. y produces the same result as y $ ints, where ints is the list of all nonnegative integers in order. Examples:
i. 5 0 1 2 3 4
A list of 5 items; the items are ascending integers.
i. 2 3 0 1 2 3 4 5
A rank-2 result.
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
A rank-3 result.
i. 0
A list of 0 items.
i. _5 4 3 2 1 0
When the argument is negative, the absolute value is used for the shape, but the items run in reverse order.
>> << Pri JfC LJ Phr Dic Voc !: Rel NuVoc wd Help J for C Programmers