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$
8
  • 1
    $\begingroup$ The solution likely would use Operate. $\endgroup$
    – QuantumDot
    Commented Jan 23, 2019 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$ Commented Jan 23, 2019 at 6:29
  • $\begingroup$ How about generalizing the question to taking a[b[c[d[...]]]] to a[b][c][d]...? $\endgroup$ Commented Jan 23, 2019 at 6:40
  • 1
    $\begingroup$ @b3m2a1: Oh.... well I recommend you alter the question... and seem my new solution. $\endgroup$ Commented Jan 23, 2019 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
    Commented Jan 23, 2019 at 9:14

4 Answers 4

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$
25
$\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$
8
  • 1
    $\begingroup$ No Operate, No # and no _, a clear winner here :P $\endgroup$
    – Kuba
    Commented Jan 23, 2019 at 9:20
  • $\begingroup$ Oh A+ that's slick $\endgroup$
    – b3m2a1
    Commented Jan 23, 2019 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
    Commented Jan 23, 2019 at 9:22
  • 1
    $\begingroup$ @evanb Compose will do as well. $\endgroup$
    – Kuba
    Commented Jan 23, 2019 at 9:37
  • 3
    $\begingroup$ +1, ++clever. yet another variant: Level[test, {-1}, HeadCompose, Heads -> True] $\endgroup$
    – WReach
    Commented Jan 23, 2019 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$
3
  • $\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
    Commented Jan 23, 2019 at 10:19
  • $\begingroup$ Or perhaps a[b[c[d[e[f]]]]] //. a_[b_[c_]] :> a[b][c]. $\endgroup$
    – march
    Commented Jan 23, 2019 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
    Commented Jan 23, 2019 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 and acknowledge you have read our privacy policy.

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