18
$\begingroup$

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]]
$\endgroup$
  • 1
    $\begingroup$ The solution likely would use Operate. $\endgroup$ – QuantumDot Jan 23 at 6:20
  • $\begingroup$ f = Curry[Replace][a_[b_[c_]] :> a[b][c]] also works, so that a[b[c]] // f gives the desired result. $\endgroup$ – Shredderroy Jan 23 at 6:29
  • $\begingroup$ How about generalizing the question to taking a[b[c[d[...]]]] to a[b][c][d]...? $\endgroup$ – David G. Stork Jan 23 at 6:40
  • 1
    $\begingroup$ @b3m2a1: Oh.... well I recommend you alter the question... and seem my new solution. $\endgroup$ – David G. Stork Jan 23 at 6:49
  • 1
    $\begingroup$ 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? $\endgroup$ – Kuba Jan 23 at 9:14
10
$\begingroup$
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]

$\endgroup$
24
$\begingroup$
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]
$\endgroup$
  • 1
    $\begingroup$ No Operate, No # and no _, a clear winner here :P $\endgroup$ – Kuba Jan 23 at 9:20
  • $\begingroup$ Oh A+ that's slick $\endgroup$ – b3m2a1 Jan 23 at 9:20
  • 3
    $\begingroup$ One issue is that this will die on nested stuff like my example case, but I still like how clean this is. You could even get away with HeadCompose @@ Level[test, {-1}, Heads -> True] $\endgroup$ – b3m2a1 Jan 23 at 9:22
  • 1
    $\begingroup$ @evanb Compose will do as well. $\endgroup$ – Kuba Jan 23 at 9:37
  • 3
    $\begingroup$ +1, ++clever. yet another variant: Level[test, {-1}, HeadCompose, Heads -> True] $\endgroup$ – WReach Jan 23 at 22:49
10
$\begingroup$

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.

$\endgroup$
  • $\begingroup$ 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. $\endgroup$ – Roman Jan 23 at 10:19
  • $\begingroup$ Or perhaps a[b[c[d[e[f]]]]] //. a_[b_[c_]] :> a[b][c]. $\endgroup$ – march Jan 23 at 16:51
  • $\begingroup$ @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. $\endgroup$ – Roman Jan 23 at 17:47
5
$\begingroup$

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]]]]
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.