In response to my request for a solution to the alternating binary array 
problem that uses J's Sequential Machine (;:) verb, Raul Miller posted this[1]

Here's an implementation which uses ;:

smalt=: 12 > (3;1,~&>0 1 2 3,1 3 2 3,2 1 3 3,:3)&;:@:#.@|:

I thought it would be worth writing Raul's solution up, for my own benefit and 
for any other people trying to learn how to use Sequential Machine in J.  I 
will try to remain faithful to the spirit of Raul's original, except in a one 
place where I thought something could be clarified, and I tend to put spaces 
and parentheses around things to break up the phrases because I'm still 
learning J and find the line noise hard to read.

The key idea is to represent a finite state machine that tracks whether the last seen 
pair of inputs is 0 1 or 1 0.  There are 4 states: if we haven't seen either a 0 1 or a 1 
0; if the last alternating pair was a 0 1; if the last alternating pair was a 1 0; and if 
we've seen a pair that violates the "alternating" rule.

In Karnough maps, if we call the elements of the inputs A and B, and the 
states, respectively, Before, Bwas1, Awas1, and Error, the state transitions are

          |     B
  Before: | 0       1
      ----+--------------
        0 | Before  Bwas1
      A   |
        1 | Awas1   Error

          |     B
  Bwas1:  | 0       1
      ----+--------------
        0 | Bwas1   Error
      A   |
        1 | Awas1   Error

          |     B
  Awas1:  | 0       1
      ----+--------------
        0 | Awas1   Bwas1
      A   |
        1 | Error   Error

          |     B
  Error:  | 0       1
      ----+--------------
        0 | Error   Error
      A   |
        1 | Error   Error

For example: if we are in state Before and see an A of 0 and a B of 1, then we 
transition to state Bwas1; if we see an A of 1 and a B of 0, then we transition 
to state Awas1; an A of 0 and B of 0 leaves us in state Before; an A of 1 and a 
B of 1 takes us to state Error.  The other states are similar.  The state 
transition diagram is the program for our machine.  Now we have to deal with 
the data for the machine.

Since the J Sequential Machine doesn't read pairs of 0's and 1's as input, but 
instead reads successive elements of the input, we encode the inputs by 
treating the pairs as numbers in base 2.  If we Laminate the A and B input 
vectors (A ,: B) we can convert each pair to a number with a (BaseTwo Atop 
Transpose) conjunction, the (#. @ |:) from Raul's implementation, as in

     A =: 1 0 0 1 0 1      NB. A first vector of binary digits.
     B =: 0 1 0 0 1 0      NB. A second vector of binary digits.
     A ,: B                NB. Laminate those together into a 2xN matrix.
  1 0 0 1 0 1
  0 1 0 0 1 0
     |: (A ,: B)           NB. Transpose to an Nx2 matrix.
  1 0
  0 1
  0 0
  1 0
  0 1
  1 0
     (#. @ |:) (A ,: B)    NB. Convert each row of the transpose from base 2.
  2 1 0 2 1 2

(Note that none of the parentheses in the last expression above is needed.  I 
put them in to emphasize that (A ,: B) is computed first, then the resulting 
noun is supplied as the single argument to the monadic conjunction (#. @ |:), 
which applies Transpose (|:) to the argument and then applies Base 2 (#.) to 
the result of the transpose.)

Similarly, we encode the state names as numbers: Before as 0, Bwas1 as 1, Awas1 
as 2, and Error as 3, and index the state transitions with the numbers formed 
by the input pairs.  That is, the state transitions are represented by the 4x4 
matrix from Raul's implementation

     0 1 2 3,1 3 2 3,2 1 3 3,:3
  0 1 2 3
  1 3 2 3
  2 1 3 3
  3 3 3 3

with the rows indexed by states and the columns indexed by inputs, yielding the 
next state.  (It's worth noting that Raul uses Laminate (,:) to build a 2x4 
matrix using the automatic reshaping of Laminate to replicate the 3 in the 
right argument to the shape of the left argument.  He could have replicated the 
3's himself, which might have been clearer than depending on the implicit 
replication.  On the other hand, one could read that single 3 as reinforcing 
the idea that once the finite state machine enters the Error state, it stays in 
that state.  Once he has formed the 2x4 matrix with the Laminate, the 
successive Append (,) verbs add their left argument vectors as additional rows 
to the top of the matrix to form the resulting 4x4 matrix.)

The J Sequential Machine (;:) verb needs more information than just the state 
transition table.  See the Dictionary for details[2], but the verb needs: a 
function code, an input mapping function, an initial iteration counter, an 
initial index into the input, the initial state, and an end-of-input action.  
These are all combined into the left argument to Sequential Machine.  In 
particular, we want a function code of 3, and an output table of 1's.  In 
Raul's implementation that's done with

     3;1,~&>0 1 2 3,1 3 2 3,2 1 3 3,:3
  +-+---+
  |3|0 1|
  | |1 1|
  | |2 1|
  | |3 1|
  | |   |
  | |1 1|
  | |3 1|
  | |2 1|
  | |3 1|
  | |   |
  | |2 1|
  | |1 1|
  | |3 1|
  | |3 1|
  | |   |
  | |3 1|
  | |3 1|
  | |3 1|
  | |3 1|
  +-+---+

(I have to say that it took me a long time to figure out what the Open (>) verb was doing in 
there.  What's needed is to Append (,) a 1 to the individual elements of the 4x4 state 
transition matrix, to produce a 4x4x2 matrix.  Using Passive Append (,~) seems somewhat 
backwards to me, as the 1 wants to be Appended to the right of elements of the state transition 
table, so it seemed like the 1 should be the right argument to the Append.  Furthermore, the 
Passive Append is used in a Compose (&) conjunction, with Open (>) as the right verb.  
The Dictionary entry for Open[3] says it has no effect when applied to an array with no boxed 
elements.  The state transition table has no boxed elements.  So why the Open, and why does that 
have to be performed before the Passive Append?  The answer is that Open is used here not for 
its verb, but for its rank, which is 0 0 0.  The Compose (&) with a right verb of Open 
(>) says that the state transition matrix is accessed by individual elemen
t, and then the result of that is Passive Appended to a 1.  To me it would be 
more natural to use Append rather than Passive Append, and to say that it 
should be applied to individual elements explicitly, with a Rank conjunction, 
as in

     (0 1 2 3 , 1 3 2 3 , 2 1 3 3 ,: 3) (,"0) 1
  0 1
  1 1
  2 1
  3 1

  1 1
  3 1
  2 1
  3 1

  2 1
  1 1
  3 1
  3 1

  3 1
  3 1
  3 1
  3 1

The verb with the rank conjunction has to be separated from the right argument to 
keep the parser from including the rank in the right argument.  I use parentheses to 
do that, but I've seen other idioms, e.g., Same (]) used for that effect.  I've seen 
the Append Rank 0 verb (,"0) in the Dictionary (e.g., on the page for Append), 
so it's not heresy to use it.)

When we Bond (&) our machine description to the Sequential Machine verb (;:) we 
get a complete, specific, sequential machine verb

     ( 3 ; ( 0 1 2 3 , 1 3 2 3 , 2 1 3 3 ,: 3 ) (,"0) 1 ) & ;:
  +------------------+-+--+
  |+-+---++---------+|&|;:|
  ||3|0 1||0 _1 0 _1|| |  |
  || |1 1||         || |  |
  || |2 1||         || |  |
  || |3 1||         || |  |
  || |   ||         || |  |
  || |1 1||         || |  |
  || |3 1||         || |  |
  || |2 1||         || |  |
  || |3 1||         || |  |
  || |   ||         || |  |
  || |2 1||         || |  |
  || |1 1||         || |  |
  || |3 1||         || |  |
  || |3 1||         || |  |
  || |   ||         || |  |
  || |3 1||         || |  |
  || |3 1||         || |  |
  || |3 1||         || |  |
  || |3 1||         || |  |
  |+-+---++---------+| |  |
  +------------------+-+--+

with the default value for m (the column of empty (cf. 0$0) to the right of the 
state transition table) and the default value for ijrd (0 _1 0 _1) as shown by 
the boxed display of non-nouns, with the ((9!:3) 2) display function[4].

When we compose that particular sequential machine verb with the verb that 
encodes the inputs, the current state (initially 0) is used to index a plane of 
the state transition table, and the encoded input is used to index a row of the 
state.  The output is the next state for the sequential machine.  For our 
sample input

      ( ( (3 ; (0 1 2 3 , 1 3 2 3 , 2 1 3 3 ,: 3) (,"0) 1) & ;: ) @: (#. @ |:) 
) (A ,: B)
  10

(That is perhaps too many parentheses even for me, but I want to emphasize that 
we are composing the verb for our sequential machine with the verb that encodes 
the input, and applying that new verb to the laminated input vectors.  Raul's 
implementation has the minimum parentheses, but as a newb I find it hard to 
read long verb trains.)

We get as output the state 10 (which encodes that the last state was Awas1, and 
that the last input was a 1 0.  We can track the individual state changes by 
tediously applying the verb to successively longer sequences of the input

     ( ( (3 ; (0 1 2 3 , 1 3 2 3 , 2 1 3 3 ,: 3) (,"0) 1) & ;: ) @: (#. @ |:) ) 
(1 ,: 0)
  10
     ( ( (3 ; (0 1 2 3 , 1 3 2 3 , 2 1 3 3 ,: 3) (,"0) 1) & ;: ) @: (#. @ |:) ) 
(1 0 ,: 0 1)
  5
     ( ( (3 ; (0 1 2 3 , 1 3 2 3 , 2 1 3 3 ,: 3) (,"0) 1) & ;: ) @: (#. @ |:) ) 
(1 0 0 ,: 0 1 0)
  4
     ( ( (3 ; (0 1 2 3 , 1 3 2 3 , 2 1 3 3 ,: 3) (,"0) 1) & ;: ) @: (#. @ |:) ) 
(1 0 0 1 ,: 0 1 0 0)
  10
     ( ( (3 ; (0 1 2 3 , 1 3 2 3 , 2 1 3 3 ,: 3) (,"0) 1) & ;: ) @: (#. @ |:) ) 
(1 0 0 1 0 ,: 0 1 0 0 1)
  5
     ( ( (3 ; (0 1 2 3 , 1 3 2 3 , 2 1 3 3 ,: 3) (,"0) 1) & ;: ) @: (#. @ |:) ) 
(1 0 0 1 0 1 ,: 0 1 0 0 1 0)
  10

or by changing the function code from 3 to 5 to get tracing of the state of the 
sequential machine

     ( ( (5 ; (0 1 2 3 , 1 3 2 3 , 2 1 3 3 ,: 3) (,"0) 1) & ;: ) @: (#. @ |:) ) 
(A ,: B)
  0 _1 0 2 2 1
  1  0 2 1 1 1
  2  1 1 0 1 1
  3  2 1 2 2 1
  4  3 2 1 1 1
  5  4 1 2 2 1

where the columns are (quoting the Dictionary, but adding a pair of pedagogical 
parentheses)

  i , j , r , c , (s {~ < r,c)

For example, on the line where i is 5: j is 4, r is 1, c is 2, and the state 
transition table at ( < 1 2 ) is ( 2 1 ).  Reading down the r column of that 
output shows us the successive states of the machine (0 2 1 1 2 1), reading down 
the c column shows us the successive encoded inputs (2 1 0 2 1 2), and reading 
down the first of the s columns shows us the next state of the machine (2 1 1 2 1 
2).

If at the end of the input the state is not in the Error state, then the 
machine has seen alternating binary inputs.  That condition is tested by 
whether the output of our machine is less than 12 (the beginning of the Error 
state), e.g., from Raul Miller's implementation

  12 > (3;1,~&>0 1 2 3,1 3 2 3,2 1 3 3,:3)&;:@:#.@|:

(I would write that as

  12 > ( ( (3 ; (0 1 2 3 , 1 3 2 3 , 2 1 3 3 ,: 3) (,"0) 1) & ;: ) @: (#. @ |:) 
)

but I expect to learn, eventually, to read J without all those extra 
parentheses.)

Thank you for reading this far.

           ... peter

P.S. All that said, I think I now prefer Marshall Lochbaum's solution

  (-: (0 1,:1 0)$~#)@:(0 0 -.~ |:)@:/:~

so I'll write that up next.

--------
[1] http://jsoftware.com/pipermail/programming/2012-July/028838.html
[2] http://www.jsoftware.com/help/dictionary/d332.htm
[3] http://www.jsoftware.com/help/dictionary/d020.htm
[4] http://www.jsoftware.com/help/dictionary/dx009.htm

----------------------------------------------------------------------
For information about J forums see http://www.jsoftware.com/forums.htm

Reply via email to