- •Foreword
- •1. Introduction
- •2. Culture Shock
- •3. Preliminaries
- •Notation Used in This Book
- •Terminology
- •Sentences (statements)
- •Word Formation (tokenizing rules)
- •Numbers
- •Characters
- •Valence of Verbs (Binary and Unary Operators)
- •How Names (Identifiers) Get Assigned
- •Order of Evaluation
- •How Names Are Substituted
- •What a verb (function) looks like
- •Running a J program
- •The Execution Window; Script Windows
- •Names Defined at Startup
- •Step-By-Step Learning: Labs
- •J Documentation
- •Getting Help
- •4. A First Look At J Programs
- •Average Daily Balance
- •Calculating Chebyshev Coefficients
- •5. Declarations
- •Arrays
- •Cells
- •Phrases To Memorize
- •Constant Lists
- •Array-creating Verbs
- •6. Loopless Code I—Verbs Have Rank
- •Examples of Implicit Loops
- •The Concept of Verb Rank
- •Verb Execution—How Rank Is Used (Monads)
- •Controlling Verb Execution By Specifying a Rank
- •Examples Of Verb Rank
- •Negative Verb Rank
- •Verb Execution—How Rank Is Used (Dyads)
- •When Dyad Frames Differ: Operand Agreement
- •Order of Execution in Implied Loops
- •A Mistake To Avoid
- •7. Starting To Write In J
- •8. More Verbs
- •Arithmetic Dyads
- •Boolean Dyads
- •Min and Max Dyads
- •Arithmetic Monads
- •Boolean Monad
- •Operations on Arrays
- •9. Loopless Code II—Adverbs / and ~
- •Modifiers
- •The Adverb Monad u/
- •The adverb ~
- •10. Continuing to Write in J
- •11. Boxing (structures)
- •Terminology
- •Boxing As an Equivalent For Structures In C
- •12. Compound Verbs
- •Verb Sequences—u@:v and u@v
- •Making a Monad Into a Dyad: The Verbs [ and ]
- •Making a Dyad Into a Monad: u&n and m&v
- •13. Empty Operands
- •Execution On a Cell Of Fills
- •Empty cells
- •If Fill-Cells Are Not Enough
- •14. Loopless Code III—Adverbs \ and \.
- •15. Verbs for Arithmetic
- •Dyads
- •Monads (all rank 0)
- •16. Loopless Code IV
- •A Few J Tricks
- •Power/If/DoWhile Conjunction u^:n and u^:v
- •Tie and Agenda (switch)
- •17. More Verbs For Boxes
- •Dyad ; (Link) And Monad ; (Raze)
- •Dyad { Revisited: the Full Story
- •Split String Into J Words: Monad ;:
- •Fetch From Structure: Dyad {::
- •Report Boxing Level: Monad L.
- •18. Verb-Definition Revisited
- •What really happens during m :n and verb define
- •Compound Verbs Can Be Assigned
- •Dual-Valence verbs: u :v
- •The Suicide Verb [:
- •Multi-Line Comments Using 0 :0
- •Final Reminder
- •The Obverse u^:_1
- •Apply Under Transformation: u&.v and u&.:v
- •Defined obverses: u :.v
- •An observation about dyadic verbs
- •20. Performance: Measurement & Tips
- •Timing Individual Sentences
- •Compounds Recognized by the Interpreter
- •Use Large Verb-Ranks! and Integrated Rank Support
- •Shining a Light: The J Performance Monitor
- •21. Input And Output
- •Foreigns
- •File Operations 1!:n; Error Handling
- •Treating a File as a Noun: Mapped Files
- •Format Data For Printing: Monad And Dyad ":
- •Format an Array: 8!:n
- •Format binary data: 3!:n
- •printf, sprintf, and qprintf
- •Convert Character To Numeric: Dyad ".
- •22. Calling a DLL Under Windows
- •Memory Management
- •Aliasing of Variables
- •23. Socket Programming
- •Asynchronous Sockets and socket_handler
- •Names and IP Addresses
- •Connecting
- •Listening
- •Other Socket Verbs
- •24. Loopless Code V—Partitions
- •Find Unique Items: Monad ~. and Monad ~:
- •Apply On Subsets: Dyad u/.
- •Apply On Partitions: Monad u;.1 and u;.2
- •Apply On Specified Partitions: Dyad u;.1 and u;.2
- •Apply On Subarray: Dyad u;.0
- •Apply On All Subarrays: Dyad u;.3 and u;._3
- •Extracting Variable-Length Fields Using ^: and ;.1
- •Example: Combining Adjacent Boxes
- •25. When Programs Are Data
- •Calling a Published Name
- •Using the Argument To a Modifier
- •Invoking a Gerund: m`:6
- •Passing the Definition Of a Verb: 128!:2 (Apply)
- •Passing an Executable Sentence: Monad ". and 5!:5
- •26. Loopless Code VI
- •28. Modifying an array: m}
- •Monad I.—Indexes of the 1s in a Boolean Vector
- •29. Control Structures
- •while./do./end. and whilst./do./end.
- •if./do./else./end., if./do./elseif./do./end.
- •try./catch./catcht./end. and throw.
- •return.
- •assert.
- •30. Modular Code
- •Locales And Locatives
- •Assignment
- •Name Lookup
- •Changing The Current Locale
- •The Shared Locale 'z'
- •Using Locales
- •31. Writing Your Own Modifiers
- •Modifiers That Do Not Refer To x. Or y.
- •Modifiers That Refer To x. Or y.
- •32. Applied Mathematics in J
- •Complex Numbers
- •Matrix Operations
- •Calculus: d., D., D:, and p..
- •Taylor Series: t., t:, and T.
- •Hypergeometric Function with H.
- •Sparse Arrays: Monad and Dyad $.
- •Random Numbers: ?
- •Computational Addons
- •Useful Scripts Supplied With J
- •33. Elementary Mathematics in J
- •Verbs for Mathematics
- •Extended Integers, Rational Numbers, and x:
- •Factors and Primes: Monad p:, Monad and Dyad q:
- •Permutations: A. and C.
- •34. Graphics
- •Plot Package
- •2D Graphics: the gl2 Library
- •Displaying Tabular Data: the Grid Control
- •3D Graphics: OpenGL
- •35. Odds And Ends
- •Dyad # Revisited
- •Boxed words to string: Monad ;:^:_1
- •Spread: #^:_1
- •Choose From Lists Item-By-Item: monad m}
- •Recursion: $:
- •Make a Table: Adverb dyad u/
- •Cartesian Product: Monad {
- •Boolean Functions: Dyad m b.
- •Operations Inside Boxes: u L: n, u S: n
- •Comparison Tolerance !.f
- •Right Shift: Monad |.!.f
- •Generalized Transpose: Dyad |:
- •Monad i: and Dyad i:
- •Fast String Searching: s: (Symbols)
- •Fast Searching: m&i.
- •CRC Calculation
- •Unicode Characters: u:
- •Window Driver And Form Editor
- •Tacit Programming
- •36. Tacit Programs
- •37. First Look At Forks
- •38. Parsing and Execution I
- •39. Parsing and Execution II
- •The Parsing Table
- •Examples Of Parsing And Execution
- •Undefined Words
- •40. Forks, Hooks, and Compound Adverbs
- •Tacit and Compound Adverbs
- •Referring To a Noun In a Tacit Verb
- •41. Readable Tacit Definitions
- •Flatten a Verb: Adverb f.
- •Special Verb-Forms Used in Tacit Definitions
- •43. Common Mistakes
- •Mechanics
- •Programming Errors
- •44. Valedictory
- •45. Glossary
- •46. Error Messages
- •47. Index
16. Loopless Code IV
In our quest to write loopless code, we first learned about J's implicit looping, which we can use to replace loops in which the same function is performed on each cell; then we learned monad / which lets us accumulate an operation across all the items of a noun, and \ and \. which apply verbs to certain regular subsets of a noun. We now examine cases in which the operations on the cells are different, but where there is no sharing of information between cells.
A Few J Tricks
In these irregular cases, the J solution is to create an array that contains control information describing the difference between the cells, and then create a dyadic operation that produces the desired result when given a cell of control information and a cell of data. Writing code this way can seem ingeniously clever or awkwardly roundabout, depending on your point of view; we will simply accept it as a necessary part of coding in J, and we will learn to be effective with it. What follows is a hodgepodge of tricks to treat cells individually. If we were writing in C, we would use if statements, but since if by necessity involves a scalar comparison we will avoid it in J.
To add one to the elements of y whose values are even: y + 0 = 2 | y
To double all the elements of y whose values are even: y * 1 + 0 = 2 | y
To create an array whose even-numbered elements come from y and whose oddnumbered elements come from x :
(x * -.sv) + y * sv =. (#y) $ 1 0
which, homely as it is, is a standard idiom in J. This expression works only for numeric operands; for general operands we can select using a selection vector sv with
sv {"_1 x ,. y
To replace lowercase 'a' through 'f' with uppercase 'A' through 'F' in a string that contains only 'a' through 'f':
('abcdef' i. y) { 'ABCDEF'
Extending the previous example: to replace lowercase 'a' through 'f' with uppercase 'A' through 'F' leaving other characters unchanged:
(('abcdef' , a.) i. y) { 'ABCDEF' , a.
To understand this you need to know the special noun a. which is the character string containing all the ASCII characters in order. Work through a simple example until you understand how this works—it's a good example of how J thinking differs from C thinking.
A similar problem: given a list of keys y and a list of data z, with each item of y corresponding to an item of z; and another list of search keys x; and a default element
95
d : return the item in z corresponding to the item of y that matches the item of x, or d if the item of x didn't match anything in y :
(y i. x) { z , d
To evaluate the polynomial defined by x, so that if for example x is 2 1 5 the result is 5y2+y+1:
+/ x * y ^ i. # x
(and now you can see why 0^0 is 1).
To evaluate the polynomial defined by x going the other direction, so that if for example x is 2 1 5 the result is 2y2+y+5:
y #. x
The last example, due to Roger Hui, has a power and economy that amount to sorcery. Suppose you had a list, and you wanted to know, for each item in the list, how many identical items appeared earlier in the list. You could find out this way:
y =. 2 2 2 1 2 1 4 6 4 2 t - (i.~ y) { t =. /: /: y
0 1 2 0 3 1 0 0 1 4
Take a little time—maybe a long time—to see how this works. The /: /: y is an idiom we discussed earlier—did you figure it out? It gives the ordinal of each item of y, in other words the rank of the item among the items of y . If there are equal items, they will occupy a block of successive ordinals. In this example you can see that t does indeed hold the ordinals:
t
2 3 4 0 5 1 7 9 8 6
(i.~ y) takes the index of each item of y within y itself, in other words, for each item, the index of the first item with the same value:
(i.~ y)
0 0 0 3 0 3 6 7 6 0
Since the identical items of y are a block of successive ordinals, and (i.~ y) comprises indexes of first items in blocks, we can find relative positions in blocks by subtracting the ordinal of the first item with a value from the ordinals of all the other items with the same value. That is what this expression does. Lovely!
In addition to the foregoing ad hoc means of varying the operation cell-by-cell J has some language features expressly designed for that purpose:
Power/If/DoWhile Conjunction u^:n and u^:v
u^:n y has infinite rank. It applies the verb u to y, then applies u to that result, and so on, for a total of n applications of u, ; in other words u u u…(n times) y, as we see when it is used with the >: (increment) primitive:
96
>: 5
6
>:^:2 (5)
7
>:^:3 (5)
8
fndisplay gives a picture of what is happening: defverbs 'incr"0'
incr^:3 (5) +----------------+ |incr incr incr 5| +----------------+
x u^:n y also has infinite rank. It evaluates x u x u…(n times) y . A simpler way to say this is to say that it is equivalent to x&u^:n y, since x&u y is equivalent to x u y .
2 |
* 2 * 2 * 2 * 5 |
80 |
*^:4 (5) |
2 |
|
80 |
|
u^:v y and x u^:v y are defined similarly: first v is evaluated (monadically or dyadically as appropriate), and then result is used as n . Formally, u^:v y is u^:(v y) y and x u^:v y is x u^:(x v y) y . With dyad u^:v, it will be rare that x and y both make sense as an operand into both u and v, and you will usually use @:[ and @:] to cause u or v to operate on only one operand. For example, to coalesce the x+1 leading axes of y into one axis, you could use x ,/@:]^:[ y :
0 |
1 |
,/@:]^:[ i. 2 2 3 |
1 |
2 |
|
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
|
2 |
,/@:]^:[ i. 2 2 3 |
0 1 2 3 4 5 6 7 8 9 10 11
This is hardly a commonplace usage, but let's analyze it, since conjunctions are still new to us. The verb is executed as if parenthesized ((,/)@:])^:[, so the first thing executed is u^:v where u is (,/)@:] and v is [ . x [ y is just x, so x is going to tell us how many times to apply x&((,/)@:]) . Now, x&((,/)@:]) y is just the same as ,/ y, because the purpose of the @:] is to ignore the left argument that was put on by x& . We remember monad ,/ from our discussion of monad u/ : it combines the 2 leading axes of y . So, x ,/@:]^:[ y will combine the 2 leading axes of y, x times; in other words, combine the x+1 leading axes of y .
The importance of u^:n is not in applying a verb several times—usually we could just write the instances out if we needed to—but rather in 4 special values of n : _1, 0, 1, and _ (infinity). u^:0, meaning apply u 0 times, is simple: it does nothing, with the result y in both dyadic and monadic forms. u^:1 means apply u once. Thinking about
97
that, we see that if n is restricted to the values 0 or 1, ^:n means 'If n' : u^:n y is y, but modified by application of u if n is 1; in C terms, it is n ? u(y) : y . If we want to apply the verb u only on the items of y for which x is 1, we can write
x u@:]^:["_1 y :
1 0 0 1 0 >:@]^:["_1 (1 2 3 4 5) 2 2 3 5 5
When n is , u^: means 'apply u repeatedly until the result stops changing'; in C, it resembles while(u(y)!=y)y = u(y); . You could use this to perform a numerical calculation that converges on a result; for example if you take …cos(cos(cos(cos(y)))) until the result stops changing, you get the solution of the equation y=cos(y):
2 o.^:_ (0)
0.739085
Another use of u^:_ is to apply an improvement u to y repeatedly until no further improvement is possible. The most important use of ^:_, though, is in the special form u^:v^:_ . Consider this form (either monad or dyad), with the understanding that v always produces a Boolean result of 0 or 1 . It is parenthesized (u^:v)^:_, i. e. u^:v repeated until its result is the same as its right operand. Now, if v evaluates to 0, the result of u^:v will certainly be the same as its right operand because u will not be executed; if v is 1, u^:v causes u to be executed once. So this construct is like C's while(v(y))y = u(y); (except that the J version also stops if the result of u y is the same as y, so the complete definition is while(v(y)&&(u(y)!=y))y = u(y); ). The great thing about having a verb to do this loop rather than a statement is that we can give the verb a rank and apply it to cells, with independent loop control on each cell:
2 *^:(100&>@:])^:_"0 (1 3 5 7 9 11) 128 192 160 112 144 176
Read this as 'for each atom of y, double it as long as the value is less than 100'. u^:_1 is also of great interest but we will discuss it later.
One last point:
>:^:1 2 4 (5) 6 7 9
As you can see, n may be an array, in which case u^:n1 y is repeatedly evaluated, with n1 assuming the value of each atom of n, and the results are assembled using the shape of n as the frame and with framing fills added as needed. Pop quiz: express the preceding English sentence in J.
Solution: u^:n y is equivalent to n u@:]^:["0 _ y, and x u^:n y is equivalent to n x&u@:]^:["0 _ y . If you can make sense of the answer, you should be content with your progress. If you came up with either half on your own, you are entitled to claim Apprentice Guru status.
98
