Monday, April 18

(I started this entry with little time, because of a short lunch meeting, and am coming back to it after a weekend, I hope it's not too disjointed. Not that I necessarily need any help, in being disjointed...)

In TDD bowling, and J, revised, part3..., I had said that I wanted to come back and write up something about how J interprets some of that code. But I have had some other thoughts since then, and let's see how this comes out.


First off, here is the code:
 nextFrameIndex=: ] + 1 + 10 > {.@{~
 limitSpares=: ] * 1,.1,.10 <: +/ .*&1 1 0
 bowlingScores=: limitSpares@({~ nextFrameIndex^:(i.10)&0)@(3 ]\ ])@{.~&21


 isScore=: -:&(+/@,) , (10 3 >: $@]) , 2 -: #@$@]
 assert 300 isScore bowlingScores 12#10
 assert 190 isScore bowlingScores 21$9 1
 assert 0 isScore bowlingScores 20$0
 assert 90 isScore bowlingScores 20$8 1


So, here is the vocabulary:

# $ & ( ) * + , ,. -: . / 0 1 1 1 0 10 10 3 12 190 2 20 21 3 300 8 1 9 1 90 <: =: > >: @ \ ] ^: assert bowlingScores i. isScore limitSpares nextFrameIndex { {. ~


That's a bit mixed up though, because a sequence of numbers in J counts as one word. And, yes, this means that you can have a word in J which contains a space. But that in and of itself is not very novel -- most languages will treat a quoted string as a single "token". But in J, you need other words to separate numeric lists. 1 2 + 3 4 would be three words. And, my presentation, above, does not really show word boundaries for numbers (but I have played with bold, to help mark off the different numbers that I used).


Anyways, numbers just represent themselves.


Next, here is an informal rundown on the words as used here. Feel free to skip over it -- it's for referring back to, when I get into examples, in case you feel lost.


This initial summary will be a bit ambiguous, to keep from getting bogged down in details. I will give some examples (later) and references to hopefully help deal with the ambiguities. I will also include links into J's dictionary that might also help.


Monadic verbs (traditional "functions" with their arguments on the right):


   #    Gives the number of items in a list
   +/  derived verb meaning "sum the items in a list"
   ,    Removes any extra structure from an array, turning it into a flat list
   ]    Identity function: its result is its argument
   i.   List of indices: 0 1 2 3 ... with size of result specified by argument
   {.  The first item of a list


   bowlingScores given a list of ball scores from a bowling game, arranges them in rows and columns, with one row for each frame, such that the sum of the entire result will be the score of the game.
   limitSpares given an array representing the frames from a bowling game, makes sure that only the first two balls count when the frame is neither a spare nor a strike (when you have a spare or a strike, you need a sequence of three balls to get the score)


Dyadic verbs (traditional "operations", like +, with arguments on left and right). I will use x as a placeholder for the left argument and y for the right argument.:


   #    Make x copies of y (if they both are sequences pair corresponding elements)
   $    make an x dimensional array from y (keep repeating y as many times as you need)
   *    x*y is x times y  (x1,x2,x3)*(y1,y2,y3) is ((x1*y1),(x2*y2),(x3*y3))
   ,    make a list with the elements from x followed by the elements from y
   ,.   like , but x and y represent columns
   -:    1 when x and y as a whole are identical, 0 otherwise
   <:   1 when an element of x is less than or equal to an element of y, 0 otherwise
   >    1 when an element of x is greater than an element of y, 0 otherwise
   >:   1 when an element of x is greater than or equal to an element of y, 0 otherwise
   ]    right identity: its result is its right argument
   {    elements from y indexed by x
   {.   the prefix of y selected by x (if x is greater than #y, pad the result)
   
   nextFrameIndex if x is a list of potential frames (or just a list of ball scores), and y is the index of one that we know is a valid frame, the result is the index of the frame after y. 


Adverbs (these modify a verb). I will use u as a placeholder for the verb (which always appears to the left of the adverb):


Adverbs used monadically:
   /    Insert verb between the items of y:  +/1 2 3 gives the result of evaluating 1+2+3


Adverbs used dyadically:
   \    apply u to each of the sublists of y which contain x elements
   ~    swap the arguments:  x u~ y is the same as y u x


Conjunctions (these are combining words that work on verbs, they bind very tightly). I will use u for the left verb and v for the right verb, here. But conjunctions can also consume a noun instead of a verb, so I will use m for a left noun and n for a right noun. Some conjunctions behave very differently with nouns.


   &    When given two verbs, the verb on the right (v) becomes the preprocessor for the verb on the left (u). In the dyadic case, v is used on each argument and those results are combined using u.
   &    When given a verb and a noun, the verb (u or v) is curried with that noun (n or m). The resulting monadic verb looks for its remaining argument on its right.


   .    This is a special conjunction for defining inner products. The usual matrix multiplication inner product is +/ .* (and you have to have a space to the left of the decimal).


   @    When given two verbs, the verb on the left (u) becomes the postprocessor for the verb on the right (v). In the dyadic case, v is used to combine the left and right arguments.


   ^:    The verb on the left (u) gets used repeatedly, the number of times it gets used is specified by the noun on the right (n). If n is 0, y is the result. If n is 1, the verb is used once, just like normal. If n is 2, the verb is used twice with the result of the first evaluation being the right argument for the second evaluation. (In a dyadic context, x will be used, unchanged, for each evaluation). f n is a list of numbers the result has an item corresponding to each element of n.


(Note, by the way, that adverbs and conjunctions in English also tend to be rather contextual in their meanings.)


But enough of this reference stuff, let's get to some examples (if you were skipping over the vocabulary material, here is a good place to stop skipping).


   nextFrameIndex=: ] + 1 + 10 > {.@{~


What does this look like, in action?

   (12$10) (] + 1 + 10 > {.@{~) 7
8


Um... ok, what did that tell us?


Here, the left argument represents ball scores and the right argument represents an index into them that marks the begining of a frame. The result will be an index either 1 or 2 greater than the right argument. It's 1 greater when the index selects a ball that knocked down 10 pins, otherwise it's 2 greater.


Let's break this down into simpler expressions:


   (12$10) ({~) 7
10


Here, we are selecting a ball which had a score of 10.



   (12$10) ({.@{~) 7
10

Here, we are selecting a ball which had a score of 10.


Hey, wait a minute!  What was the point of that extra stuff?

A problem here is that I gave the left argument in a form that the code did not actually use. In "real life" it would have looked more like this:

   (10 3$10) (] + 1 + 10 > {.@{~) 7
1

In other words, I would have given it 10 rows and each row would represent one frame. So, let's start over:

   (10 3$10) ({~) 7
10 10 10

The eighth frame had three balls and all of them were perfect scores. (As is usual for indices in most modern programming languages the first value is selected by the index 0. Here, "first" would be a cardinal number and "0" an ordinal number.)

   (10 3$10) ({.@{~) 7
10

The first ball of the eighth frame was a perfect 10.

   (10 3$10) (10 > {.@{~) 7
0

10 is not greater than the score of the first ball in the eighth frame


   (10 3$10) (1 + 10 > {.@{~) 7
1

The offset to the beginning of the next frame is 1.


   (10 3$10) (7 + 1 + 10 > {.@{~) 7
8

The index of the next frame is 1 (that's the ninth frame). Note that here I plugged in the value 7 for the current index. In the real code I instead used the function ] which always has as its value the value of its right argument. In other words:

   (10 3$10) (] + 1 + 10 > {.@{~) 7
8


The answer is: when we have an odd number of verbs arranged in a isolated train (and the parenthesis here are sufficient to isolate them), the odd ones get the same right argument as the train as a whole. And if the train has a whole has a left argument, all of the odd verbs get that for their left argument also where if the train as a whole has no left argument none of the odd verbs get a left argument.

Meanwhile, the even verbs are combining verbs. They combine the results from the odd verbs (working from right to left, just like J normally does -- and note also that arabic numbers are also designed to work from right to left with the least part on the right).

But wait, so how does that work when you have numbers like 1 and 10 in it?  Those are not verbs!  Those are nouns!!

The answer is:  if we use a noun in the odd positions in what would otherwise be a verb train, we treat them as verbs which always produce themselves as their result. The rightmost word has to be a verb, but the other odd positions can be nouns.

And yes, I can imagine some grumbling about how this odd and even business seems contrived. But it corresponds rather closely to some notation that winds up being used rather often in mathematics. Anyways, that's the way J works.

And, I am out of time again...


2 comments:

  1. The deeper insights you have (e.g. the odd vs. even trains) while true, may seem irrelevant to the beginner, and lose them as a result.

    I'm also wondering if the (n,3)$ that you use to structure the data may be misleading. I'm partial to using the prefix adverb in constructing the frames:
    (3({.each)<)\. rolls
    (< #: (3{.]))\.rolls

    ReplyDelete
  2. Well... in principle, I do not care much about the people that do not give me any feedback. But, of course, this leaves you as my highest priority reader.

    On the other hand, I am not likely to go back in and make major structural changes to anything here -- I lack motivation. But, if I can extract good concepts for future posts, I'll be using them.

    On the third hand, I do not think I used (n,3)$ on this post, but I do remember using it to structure some tests. And you are right that it could be misleading -- it suggests that adjacent rows can be independent when that would not be true for typical bowling games (it can only be true for games where the scores of all frames are less than 10). But tests are already special cases -- I think I would go mad if I tried to make them fully general.

    On the fourth hand, if the tests could be made simpler and clearer, I agree that that would be a good thing.

    And that's half an octopus, so I'll stop here.

    (Forewarned is forearmed, and fourarmed is half an octopus.)

    ReplyDelete