views:

84

answers:

3

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.

A: 

This version of the function I had in mind is better, but still not tacit enough to be perfect. (At least it doesn't beg the question for another problem down the road, at any rate.) Once I figure out how to represent the logic of that while loop as a tacit expression, I'll be all set.

NB. boxmerge takes a boxed argument and razes the first two
NB. elements together into a new box.
boxmerge =: [:}.]1}~[:<[:;2&{.

NB. conseq checks to see whether the first two boxes of a boxed
NB. array contain the same distinct element. (By assumption, each
NB. box contains only one distinct element.) The name is an
NB. abbreviation of the question, "consecutive boxes equal?"
conseq =: [:=/[:~.&.>2&{.

partfun =: ]`(boxmerge)@.conseq ^:_

listpack =: 3 : 0
mylist =. y
listbin =. >a:
while. (mylist -: (>a:)) = 0 do.
 newlist =. partfun mylist
 listbin =. listbin,{. newlist
 mylist =. }. newlist
end.
listbin
)

The idea behind the while loop is to apply partfun to a list, ravel its head onto a different list, behead the original list, and continue doing that until the original list has been completely emptied. I feel like there really should be a way to represent that logic with tacit expressions. (In fact, I think I've even seen it in the online documentation.) I just can't think of the appropriate sequence of ^:'s, $:'s and @.'s I need to put the final nail in the coffin.

estanford
+1  A: 

I think you're overthinking it. Does it need to be completely tacit? Here's something I just threw together:

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

Obviously it just assigns the input list to s then drops it into the ;. expression. If it needs to be completely tacit, I'm sure you can massage it to ravel the input list to to the boolean list, then use something like {. < ;.2 {: to get the output.

David
+2  A: 

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.

earl
So that's how you do that! I spent days poking around with the documentation, trying to make J apply boxes to elements using a Boolean output. Thanks for the help.
estanford
earl