Search code examples
tacit-programmingjmultidimensional-array

Abstracting boxed array structures in J


I've been working on a J function for a while, that's supposed to scan a list and put consecutive copies of an element into separate, concatenated boxes. My efforts have taken me as far as the function

(<;. 2) ((2&(~:/\)),1:)

which tests successive list entries for inequality, returns a list of boolean values, and cuts the list into boxes that end each time the number 1 appears. Here's an example application:

   (<;. 2) ((2&(~:/\)),1:) 1 2 3 3 3 4 1 1 1
+-+-+-----+-+-----+
|1|1|0 0 1|1|0 0 1|
+-+-+-----+-+-----+

The task would be finished if I could then replace all those booleans with their corresponding values in the input argument. I've been looking for some kind of mystery function that would let me do something like

   final =: mysteryfunction @ (<;. 2) ((2&(~:/\)),1:)

   final 1 2 3 3 3 4 1 1 1    
+-+-+-----+-+-----+
|1|2|3 3 3|4|1 1 1|
+-+-+-----+-+-----+

In an ideal situation, there would be some way to abstractly represent the nesting pattern generated by (<;. 2) ((2&(~:/\)),1:) and apply it to the original input list. (i.e. "This boxed array over here has the first element boxed at depth one, the second element boxed at depth one, the third, fourth, and fifth elements boxed together at depth one,..., so take that unboxed list over there and box it up the same way.") I tried fooling around with ;. , S: , L:, L. and &. to produce that behavior, but I haven't had much luck. Is there some kind of operator or principle I'm missing that could make this happen? It wouldn't surprise me if I were overthinking the whole issue, but I'm running out of ideas.

EDIT:

At the moment, the only working solution I have is this one:

isduplicate =: ((2&(~:/\)),1:)

testfun =: 3 : 0
numduplicates =. #S:0 ((<;.2) isduplicate y)
distinctboxes =. <"0 (isduplicate#]) y
numduplicates # each distinctboxes
)

It's a two-step process of generating the run-length encoding of the list and then undoing the encoding without getting rid of the boxes. Since I'm originally doing this with the aim of solving the 99 problems in tandem using J and Haskell, it feels like begging the question if I solve problem 9 by first solving problem 12.


Solution

  • You're almost there. Add a ~ and place the parentheses differently, and that's it:

       (<;.2~ (2&(~:/\) , 1:)) 1 2 3 3 3 4 1 1 1
    ┌─┬─┬─────┬─┬─────┐
    │1│2│3 3 3│4│1 1 1│
    └─┴─┴─────┴─┴─────┘
    

    A quick explanation/illustration:

       s =: 1 2 3 3 3 4 1 1 1
    
       f =: 2&(~:/\) , 1:
       f s
    1 1 0 0 1 1 0 0 1
    
       g =: <;.2
    
       (f s) g s
    ┌─┬─┬─────┬─┬─────┐
    │1│2│3 3 3│4│1 1 1│
    └─┴─┴─────┴─┴─────┘
    

    Now that final (f s) g s, sometimes referred to as a "left hook", can be written (g~ f) s (the adverb ~ is called "passive" in J, the Haskell counterpart would be flip). Alternatively you could also tacitly write this as the fork (f g ]) s.

    Chapter 9 of "Learning J" discusses this topic extensively, if you want to find out more.

    Update: I previously used a grouping-based (</.~ (+/\&(1,(2&(~:/\))))), but your original cut-based approach is more elegant (and shorter) than this. As this is really about the left hook, I updated to use your approach directly.