18

As indicated in the title I'm looking for the fastest way to transform a[b[c]] into a[b][c], and the natural generalization to an arbitrary chaining of arguments. I'm sure there's got to be a convenient way that I've overlooked.

In my cases a, b, and c can be any expression with any complicated internal structure they like.

As an example, we could have some terrible deeply nested thing like:

bleh =
  Nest[f, 10, 10]@
   Nest[b, 100, 100]@Nest[c, RandomReal[{}, {1000, 1000}], 1000];

And then we can to convert this into:

blehm =
  Nest[f, 10, 10][
   Nest[b, 100, 100]][Nest[c, RandomReal[{}, {1000, 1000}], 1000]]
Kuba
  • 136,707
  • 13
  • 279
  • 740
b3m2a1
  • 46,870
  • 3
  • 92
  • 239
  • 1
    The solution likely would use Operate. – QuantumDot Jan 23 '19 at 06:20
  • f = Curry[Replace][a_[b_[c_]] :> a[b][c]] also works, so that a[b[c]] // f gives the desired result. – Shredderroy Jan 23 '19 at 06:29
  • How about generalizing the question to taking a[b[c[d[...]]]] to a[b][c][d]...? – David G. Stork Jan 23 '19 at 06:40
  • @DavidG.Stork sorry I thought that was implicit – b3m2a1 Jan 23 '19 at 06:46
  • 1
    @b3m2a1: Oh.... well I recommend you alter the question... and seem my new solution. – David G. Stork Jan 23 '19 at 06:49
  • I'm just curious: is the reason behind the question being able to rewrite terms like a@b[c]@d as a[b[c]][d]? It's just that I have been looking for a left-associative counterpart of @ for some time and this question is just what I need! – Anton.Sakovich Jan 23 '19 at 08:19
  • 1
    If you want a b c to be any expression you better show some examples, what is a[b[c], d[e, f]] supposed to be converted to? – Kuba Jan 23 '19 at 09:14
  • @Kuba that's exactly what I wrangled with in the way I implemented this. I don't really know what to do with multi argument functions. So I assumed a chain of one-argument functions. At this point I'm not sure my question is well-defined though so I'm just enjoying people's creativity and up-voting everything. – b3m2a1 Jan 23 '19 at 09:17

4 Answers4

24
test = a[b[c[d]]];

Fold[
  Construct,   (* or Compose, see [1] *)
  Level[test, {-1}, Heads -> True]
]
a[b][c][d]

[1] - Is there a name for #1@#2&?

Alternatively, thanks to OP and Mr.Wizard:

HeadCompose @@ Level[test, {-1}, Heads -> True]
Kuba
  • 136,707
  • 13
  • 279
  • 740
10
Operate[#[[0]], First@#] &[a[b[c]]]

a[b][c]

ClearAll[deCompose]
deCompose = Nest[Operate[#[[0]], First@#] &, #, Depth[#] - 2] &;

deCompose@a[b[c]]

a[b][c]

exp = Compose[a, b, c, d, e, f, g]

a[b[c[d[e[f[g]]]]]]

deCompose @ exp

a[b][c][d][e][f][g]

kglr
  • 394,356
  • 18
  • 477
  • 896
10

This works on any level:

a[b[c]] //. x_[s : _[_]] :> Operate[x, s]
(* a[b][c] *)

a[b[c[d[e[f[g[h]]]]]]] //. x_[s : _[_]] :> Operate[x, s]
(* a[b][c][d][e][f][g][h] *)

simpler syntax but same thing:

a[b[c[d[e[f[g[h]]]]]]] //. x_[y_[z_]] -> x[y][z]
(* a[b][c][d][e][f][g][h] *)    

Of course, if the components a, b, c etc. have such complicated internal structure that they match the pattern x_[s:_[_]] (equivalent to x_[y_[z_]]), then this proposed solution will fail by over-matching. This could be remedied by constraining the pattern and fixing which elements must be atomic with _?AtomQ. It all depends on the use case.

This way of pattern matching can also be expanded to specific other situations like a[b[c],d[e]] etc., depending on what result is desired.

Roman
  • 47,322
  • 2
  • 55
  • 121
  • For your terribly nested example, what characterizes the composition is that none of the components are atomic. With NotAtomQ[x_] := !AtomQ[x] we have blehm == bleh //. x_?NotAtomQ[s : _?NotAtomQ[_?NotAtomQ]] :> Operate[x, s]. This breaks the a[b[c]] example though. I find that pattern matching gives more fine-grained control than Level in this situation. – Roman Jan 23 '19 at 10:19
  • Or perhaps a[b[c[d[e[f]]]]] //. a_[b_[c_]] :> a[b][c]. – march Jan 23 '19 at 16:51
  • @march that's exactly the same as I wrote, just using a slightly different syntax. Yes maybe your version is clearer, sorry for being so cryptic. – Roman Jan 23 '19 at 17:47
5

Not particularly "clean," but it works:

Operate[Head[#], Level[#, 2][[2]]] & @ a[b[c]]

For the full generalization:

Nest[Operate[Head[#], Level[#, 2][[2]]] & , #, Depth[#] -2] & @
 a[b[c[d[e]]]]
David G. Stork
  • 41,180
  • 3
  • 34
  • 96