I've seen the operation described in the title before (basically, swap the heads of the first two levels of an expression), but I can't find it...

Does anyone know the function (or idiom/incantation) for the operation I'm talking about?

Note that, in general, Thread doesn't do the described operation. E.g.:

Thread[X[Y[1, 2, 3]], Y]

evaluates to

Y[X[1], X[2], X[3]]

rather than the desired

Y[X[1, 2, 3]]

The same thing goes for Map:

Map[X, Y[1, 2, 3]]

(* Y[X[1], X[2], X[3]] *)

I've tried many, many, many, many, many possibilities: Transpose[X[Y[1, 2, 3]], {1, 0}], Outer[X, Y[1, 2, 3], 0, Heads -> True], etc., etc.... without success, of course.

  • $\begingroup$ Would a straightforward ReplaceAll not be sufficient? $\endgroup$ – Yves Klett Jun 13 '15 at 21:02
  • $\begingroup$ @YvesKlett: when I tried replace-based solutions I didn't succeed... I'll try again some more. $\endgroup$ – kjo Jun 13 '15 at 21:03
  • 2
    $\begingroup$ /. a_[b_[c___]] :> b[a[c]] for arbitrary fn names... $\endgroup$ – ciao Jun 13 '15 at 21:08
  • $\begingroup$ @YvesKlett: I remember now: I was trying to find a solution that was agnostic about the heads in question, and every replacement-based solution I could come up with required me to hard-code the specific heads I wanted to swap. On the other hand, I can't program Mathematica patterns to save my life, so I tend to stay clear of them as much as I can... $\endgroup$ – kjo Jun 13 '15 at 21:08
  • $\begingroup$ @ciao: thanks, I think that the :> is what I was missing... $\endgroup$ – kjo Jun 13 '15 at 21:09
f[x_[y_[z___]]] := y[x[z]]

f[X[Y[a, b, c, d]]]
Y[X[a, b, c, d]]
  • $\begingroup$ Thank you! I thought I once came across a remark in the docs to the effect that some built-in function could be easily used to do this head-swapping operation, and in fact it was routinely used for this... I'll wait a bit longer to see if my memory is not tricking me. But if it is, then your solution shows that it's trivial to roll one's own. $\endgroup$ – kjo Jun 13 '15 at 21:23
  • $\begingroup$ @kjo, {X,Y} = {Y,X} will swap values. maybe this is the remark you've confused with your question? $\endgroup$ – Dan Oak Jun 14 '15 at 5:45

One way is to use ReplaceAll, as suggested by Yves. For example:

f[g[x]] /. {f -> g, g -> f}

f[g[x]] /. {g -> f, f -> g} works too.

  • $\begingroup$ Compared to /. f_[g_[x___]] -> g[f[x]] the drawback would be that the function names need to be known in advance $\endgroup$ – Yves Klett Jun 13 '15 at 21:23
  • 2
    $\begingroup$ In addition to what Yves said, I think OP wanted only the outer two heads to be swapped. So for f[g[f[g[x]]]], the desired output would be g[f[f[g[x]]]] rather than g[f[g[f[x]]]] which this answer returns. $\endgroup$ – rm -rf Jun 13 '15 at 22:06
  • 3
    $\begingroup$ @TheToad you do have a wicked sense for potential bugs - as befits any proud amphibian ;-) $\endgroup$ – Yves Klett Jun 13 '15 at 22:22
  • 1
    $\begingroup$ In the case of @The Toad's example, the fix is to use Replace[] instead: Replace[f[g[f[g[x]]]], {f -> g, g -> f}, 2, Heads -> True]. $\endgroup$ – J. M. will be back soon Jun 14 '15 at 0:07

Remembering that [[0]] returns the head of an expression you could do something like

(#[[1, 0]]@(#[[0]] @@ #[[1]])) & @ X[Y[1, 2, 3]]
(*-> Y[X[1, 2, 3]] *)

EDIT: This should now also work if X has additional arguments besides Y:

(#[[1, 0]][#[[0]][Sequence @@ #[[1]]], Sequence @@ #[[2 ;;]]]) & @ X[Y[1,2,3],4,5]
(*-> Y[X[1,2,3],4,5] *)

Sequence@@ is necessary to get rid of the original head of 1,2,3 and 4,5, since Part always wraps such element sequences in the original head.


this is craziness... EventHorizon511's approach (through Part), but swap as many heads you want

HeadsReverse[expr_, n_] := 
    Fold[#2 @ #1 &,
        #1[[1]] @@ expr[[Sequence @@ ConstantArray[1, n - 1], ;;]], 
        #1[[2 ;;]]
    ] & @ Cases[expr, _, n, n, Heads -> True]

HeadsReverse[X[Y[a, b, c]], 2]
(* Y[X[a, b, c]] *)

Table[HeadsReverse[1[2[3[u[v[x, y, z], f]]]], n], {n, 5}]
(* {
    1[2[3[u[v[x, y, z], f]]]],
    2[1[3[u[v[x, y, z], f]]]],
    3[2[1[u[v[x, y, z], f]]]],
    u[3[2[1[v[x, y, z], f]]]], 
    v[u[3[2[1[x, y, z]]]]]
} *)

still not pure craziness, because when n == 5 f in the above example is gone


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.