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 '19 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 '19 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 '19 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 '19 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 '19 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]

| improve this answer | |
$\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]
| improve this answer | |
$\endgroup$
  • 1
    $\begingroup$ No Operate, No # and no _, a clear winner here :P $\endgroup$ – Kuba Jan 23 '19 at 9:20
  • $\begingroup$ Oh A+ that's slick $\endgroup$ – b3m2a1 Jan 23 '19 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 '19 at 9:22
  • 1
    $\begingroup$ @evanb Compose will do as well. $\endgroup$ – Kuba Jan 23 '19 at 9:37
  • 3
    $\begingroup$ +1, ++clever. yet another variant: Level[test, {-1}, HeadCompose, Heads -> True] $\endgroup$ – WReach Jan 23 '19 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.

| improve this answer | |
$\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 '19 at 10:19
  • $\begingroup$ Or perhaps a[b[c[d[e[f]]]]] //. a_[b_[c_]] :> a[b][c]. $\endgroup$ – march Jan 23 '19 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 '19 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]]]]
| improve this answer | |
$\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.