2
$\begingroup$

Is there a simple function that will take expressions of the form

f[a1][a2][a3]

and will output f, for an arbitrary number of arguments a1, a2, a3, etc (also possibly zero).

Also, is there a simple function to rearrange the input to f[a1, a2, a3] (which of course would immediately solve the first question too).


My own attempt for the first problem:

takeHead[expr_] := FixedPointList[Head, expr][[-3]]

which seems to work but I don't know whether it is robust for all cases. (We can optionally add a HoldAll depending of whether we want to take the head before or after evaluation.)

$\endgroup$
2
$\begingroup$

Alternatively:

NestWhile[Head, f[a1][a2][a3], MatchQ[_[___]]]
f
NestWhile[
  Join[#[[0]], #[[0, 0]] @@ #] &
, f[a1][a2][a3]
, MatchQ[_[___][___]]
]
f[a1, a2, a3]
$\endgroup$
4
$\begingroup$

Your expression is composed of three nested functions. At the outermost level, f[a1][a2] is the Head to a3:

expr = f[a1][a2][a3];
{expr[[0]], expr[[1]]}

(* {f[a1][a2], a3} *)

As you suggested, you will get to the innermost Head, f, by applying the Head function until the result no longer changes. An elegant way to do it is to replace any function definition by its Head with ReplaceRepeated:

ReplaceRepeated[f[a1][a2][a3], h_[___] :> h]
(* f *)

For the second part of the question, just a quick and dirty solution with Sow and Reap:

Reap[
  ReplaceRepeated[f[a1][a2][a3], 
     h_[e___] :> (Sow[{e}]; h)]
] /. {func_, {arguments_}} :> func[Sequence @@ Join @@ Reverse[arguments]]

Note the braces around the argument e in Sow to support multiple arguments. Join then neutralizes this before Sequenceing the arguments together.

EDIT

And now the proper solution, kindly provided by the documentation of ref/ReplaceRepeated:

ReplaceRepeated[f[a1][a2][a3], h_[a___][b___] -> h[a, b]]
(* f[a1, a2, a3] *)
$\endgroup$
  • $\begingroup$ An equivalent (I think!) way to do this is: expr//.g_[_]->g right? actually, that's obvious after the fact :) $\endgroup$ – Ben Kalziqi Apr 16 '18 at 20:56
  • 1
    $\begingroup$ Yes, //. is the shorthand code for ReplaceRepeated. Note that g_[x_] specifies exactly one argument, so it will not match f[a1][a2,a3,a4] or f[a1][]. $\endgroup$ – Theo Tiger Apr 16 '18 at 21:00
  • $\begingroup$ May I ask if there's an advantage to using :> instead of -> in this context? $\endgroup$ – Ben Kalziqi Apr 16 '18 at 21:13
  • $\begingroup$ Indeed it does not make a difference here. $\endgroup$ – Theo Tiger Apr 16 '18 at 21:40
2
$\begingroup$

A couple other ideas to extract the head:

expr = f[a][b][c];

FirstCase[expr, _, Missing[], {-1}, Heads->True]
First @ Level[expr, {-1}, Heads->True]

f

f

$\endgroup$
1
$\begingroup$

A very naïve—but I think slightly faster—solution to the second part of your question may be to use strings, if your arguments do really look like you've proposed.

On my test expression, which is f followed by the alphabet 10 times:

expr=Fold[#1@#2 &, f, ToExpression /@ Flatten[Table[Alphabet[], 10]]];

I can just use ToString and StringReplace to take advantage of the repetitive structure:

ToExpression@StringReplace[ToString@expr, "][" -> ","]

This is about 2.5x as fast on my machine; they seem to equalize as the number of arguments increases. For short sequences of arguments, this seems faster—and is also a little easier to understand, perhaps! Theo's solution is certainly more general, but this might work for what you need.

EDIT: This does remain a little faster than the new—and very clean!— ReplaceRepeated solution, it seems.

$\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.