Newsgroups: comp.lang.apl
Path: watmath!watserv1!utgpu!news-server.csri.toronto.edu!rpi!zaphod.mps.ohio-state.edu!mips!darwin.sura.net!haven.umd.edu!socrates!socrates!rockwell
From: rockwell@socrates.umd.edu (Raul Deluth Miller-Rockwell)
Subject: Re: a puzzle for j'ers
In-Reply-To: jmlang@jeeves.waterloo.edu's message of Sun, 5 Apr 1992 01:49:09 GMT
Message-ID: <ROCKWELL.92Apr6220013@socrates.umd.edu>
Sender: rockwell@socrates.umd.edu (Raul Deluth Miller-Rockwell)
Organization: Traveller
References: <1992Apr5.014909.750@watdragon.waterloo.edu>
Date: Tue, 7 Apr 1992 03:00:13 GMT

Je'ro^me M. Lang:
   Here's something I cooked up in J, then converted to "mumble"-mode.
   I thought it could be neat to offer it as a puzzle.  So, what do
   you think this does:
   -----------------------------------------------------------
   mystery=.(((]+ 1&|.+_1&|.)("@[.)1-])@])(((3&=)@[)+((2&=)@[)*.])]
   ------------------------------------------------------------

Hmm.. lotsa parenthesis, so there are probably several independent
concepts being used.  Well, I hope I don't spoil it for everyone else
if I post what I see...

   ]   +   1&|.   +   _1&|.

That's equivalent to a function along the lines of:
   'y.  +  (1 |. y.)  +  (_1 |. y.)'

This 'adding self to neighbors' looks like something you'd see in John
Horton Conway's game of life.  Looking back at the original
expression, I see comparisons to 2 and 3, so I'll use 'the game of
life' as my working hypothesis.  Presumably, the rank operation ("@[.)
will somehow deal with extending this 'shift and add' to both rows and
columns.

A quick look at the parse tree for this expression confirms this
hypothesis.  The full parse tree is rather large, but if I replace the
shift and add expression with the name 'neighbors', I get:

                                       +- neighbors
                           +- " -------+- 1        
                     +- @ -+- neighbors            
               +-----+- ---                        
         +- @ -+     +- ]                          
         |     +- ]                                
         |                 +- 3                    
         |           +- & -+- =                    
         |     +- @ -+- [                          
         |     +- +                                
- life --+     |                       +- 2        
         +-----+           +- & -------+- =        
         |     |     +- @ -+- [                    
         |     +-----+- *.                         
         |           +- ]                          
         +- ]                                      

In fact, this gives me enough information to confirm my hypothesis.

I should get a similar parse tree if I fix the verb LIFE defined as
follows:

LIFE =.         '(neighborhood y.) compare y.'          :11
compare =.      '(3=x.) + (2=x.)*.y.'                   :11
neighborhood =. '(neighbors neighbors"1 y.) - y.'       :11
neighbors =.    'y. + (1 |. y.) + (_1 |. y.)'           :11

It's not quite the same, but it's pretty close.  And tests on both of
them, using test=. >0; 0 0 1; 0 0 1; 0 0 1; 0 0 0 0 0   as the
argument, yields the same result for both.

So, let's see.. 

The conjunction train might be of interest.  It has the form:
    neighbors ("@[.) 1 - ]

The parenthesized expression is a conjunction, so we only really need
to pay attention to the part of the expression that reads
    neighbors ("@[.) 1

which is equivalent to
    (neighbors " 1)   @  (neighbors [. 1)

which is equivalent to
    (neighbors " 1)  @ neighbors

And, putting back in the subtraction of the right argument, we get the
definition of 'neighborhood'.

Je'ro^me:
   For bonus marks, is there a shorter expression that does the same
   thing? 

Seems like there ought to be.

Let's start by changing the compare logic so there's no need to
subtract out the original configuration.  Something like
   (3 = new) +. (3 = new-old)

Rather than just writing down the tacit form, let's see what automatic
conversion does:
   '(3 = y.) +. (3 = y.-x.)'    :11
+-------------+--+-------------------+
|+-------+-+-+|+.|+-------+-+-------+|
||+-+-+-+|@|]||  ||+-+-+-+|@|+-+-+-+||
|||3|&|=|| | ||  |||3|&|=|| ||]|-|[|||
||+-+-+-+| | ||  ||+-+-+-+| |+-+-+-+||
|+-------+-+-+|  |+-------+-+-------+|
+-------------+--+-------------------+

Note that both left and right arguments to +. must be processed by 3&=
So I might write this part of the expression
   +.&(3&=) 

And, since the rest of the expression is of the form
   ] verb ] - [

I can write the "obfuscated form" of this compare logic:
   ]  +.&(3&=)   ]-[

Also, since I picked the neighbor count as the right argument of
'compare', I'll be able to write the entire expression using a hook of
the form
   compare neighborhood

This will rely on the re-write rule for monadic hooks:
   (f g)x
is
   x(f g)x
is
   x f g x
is
   x f (g x)

Putting it all together, we've got:
   life=. (]+.&(3&=)]-[)(+1&|.+_1&|.)("@[.)1

which is about 40 percent shorter than the original expression.

Of course, you can get exactly the same verb by using:
   LIFE=.               compare neighborhood
   compare=.            ]  +.&is_three  ]  -  [
   is_three=.           3&=

   neighbors=.          +  1&|.  +  _1&|.
   chainRank=.          " @[.
   neighborhood=.       neighbors chainRank 1

   Life=.  LIFE f.

If you display the definitions for 'Life' and for 'life' you will see
they are the same.

If I were documenting this as a program, I'd want to include the rules
for John Horton Conway's game of life.  So, since they aren't all that
complicated:

 Life is an example of a cellular automata.

 Cells are represented on a rectangular grid, and may exist in one of
 two states: Live or Empty.

 A cell which is alive in one generation will be alive in the next
 generation if it has either two or three live adjacent neighbors.
 If there's more than this, it dies (of overcrowding).  If there's
 less than this, it dies (of loneliness).  Dead cells will be empty in
 the next generation

 A cell which is empty in will become alive in the next generation if
 it has exactly three live adjacent neighbors.

 There are some interesting patterns which can emerge after running
 these rules for a while.  

Note that the expression given here only computes the effect of one
generation passing.  But you can extend this to many generations with
an expression similar to:
   life ^: (i. 21)   test

-- 
Raul Deluth Miller-Rockwell                   <rockwell@socrates.umd.edu>
