24
$\begingroup$

Often when I'm writing OOP code using an object-manager association I find myself doing something akin to currying the arguments to some form of delegate object or head. (Building a one-argument chained call as opposed to returning functions of one argument).

Usually I do this via a Block construct but it is the sort of simple functional programming thing that Mathematica really ought to have a built-in for.

What I mean is I have something like:

c[a1, a2, a3, ..., an]

And I would like a function PseudoCurry that upon application to the previous expression would give me:

c[a1][a2][a3][...][an]

To my deep surprise I have been unable to find such a function.

Does anyone know how I can write a one-line, functional way to do this?

I'm sure the answer is dead simple but I'm blanking on it right now.

Update

Thanks to both Bob Hanlon and Mr. Wizard for the answers.

I think this from Bob:

Pseudocurry[h_[a__]] := Fold[#1[#2] &, {h, a}];
Pseudocurry~SetAttributes~HoldFirst;

is the cleanest way to do this without using deprecated functions but Mr. Wizard's

Pseudocurry[h_[a__]] := HeadCompose[h, a];
Pseudocurry~SetAttributes~HoldFirst;

is the clear winner for simplicity, although HeadCompose is deprecated.

$\endgroup$
  • $\begingroup$ To clarify you don't need help getting the expression c[a1][a2][a3][...][an] to evaluate as you want but instead you wish to generate that expression from c[a1, a2, a3, ..., an]? $\endgroup$ – Mr.Wizard Dec 27 '16 at 2:51
  • $\begingroup$ Just use a conditional downvalue with recursion or Nest $\endgroup$ – M.R. Dec 27 '16 at 2:54
  • $\begingroup$ @Mr.Wizard exactly. For simplicity assume it's inert. $\endgroup$ – b3m2a1 Dec 27 '16 at 2:57
  • $\begingroup$ Curious side note, try: expr = c[Apply[Sequence]@Range@1000]; Pseudocurry[Evaluate@expr] (yields red MaxFormatDepthExceeded in output string ) $\endgroup$ – Sascha Dec 27 '16 at 9:14
  • $\begingroup$ Do you mean the MaxFormatDepthExceeded issue in viewing that output? That is interesting. Certainly tells us a little bit about how the front end renders expressions. $\endgroup$ – b3m2a1 Dec 27 '16 at 9:18
19
$\begingroup$

EDIT: Modified to cover situation when an argument is a List

Use Fold

expr = c[a1, a2, a3, a4, a5];

Fold[#1[#2] &, {c, List @@ expr} // Flatten[#, 1]&]

(*  c[a1][a2][a3][a4][a5]  *)

expr2 = c[a1, a2, {a31, a32, a33}, a4, a5];

Fold[#1[#2] &, {c, List @@ expr2} // Flatten[#, 1] &]

(*  c[a1][a2][{a31, a32, a33}][a4][a5]  *)
$\endgroup$
  • 2
    $\begingroup$ Ah, very nice, I thought Fold would be a good way to go. Might I suggest Prepend[List@@expr,Head@expr] instead, though, as the flatten would flatten any sublists in the ai? $\endgroup$ – b3m2a1 Dec 27 '16 at 3:01
17
$\begingroup$

The deprecated (but valid) function HeadCompose basically does just that:

c[a1, a2, a3, a4, a5] /. h_[a___] :> HeadCompose[h, a]
c[a1][a2][a3][a4][a5]

If you don't wish to use that then perhaps one of these:

f1 = FixedPoint[Replace[h_[x_, y__] :> h[x][y]], #] &;

f2 = # //. {x : _[_] :> x, h_[x_, y__] :> h[x][y]} &;

f3 @ h_[x___, y_] := f3[h[x]][y]
f3 @ h_[] := h

e.g.

c[a1, a2, a3, a4, a5] // f1
c[a1][a2][a3][a4][a5]
$\endgroup$
  • $\begingroup$ HeadCompose is almost exactly what I wanted. Any idea why it was deprecated? Unfortunately I'm rather wary of deprecated functions. $\endgroup$ – b3m2a1 Dec 27 '16 at 3:06
  • $\begingroup$ Hmm, wouldn't c[a1, a2, a3, a4, a5] //. h_[x_, y__] :> h[x][y] be simpler than having to use both FixedPoint[] and Replace[]? $\endgroup$ – J. M. is away Dec 27 '16 at 3:09
  • 1
    $\begingroup$ @MB1965 Wiser users than I assure me that functions like this aren't going away, so I use them. (e.g. ToHeldExpression; Compose) I would guess that this function was not thought to be widely useful and was dropped from the documentation. $\endgroup$ – Mr.Wizard Dec 27 '16 at 3:16
  • 1
    $\begingroup$ @Mr.Wizard good to know. I do know that WRI makes a big deal about backwards compatibility. $\endgroup$ – b3m2a1 Dec 27 '16 at 3:22
  • 3
    $\begingroup$ ?? HeadCompose still gives the basic usage. $\endgroup$ – Bob Hanlon Dec 27 '16 at 3:26
7
$\begingroup$

Also possible (any maybe more readable) using patterns and ReplaceRepeated

c[a1, a2, a3, a4, a5]  //. f_[most__, last_] :> f[most][last]
c[a1][a2][a3][a4][a5]

As indicated by @MB1965 in a comment ReplaceRepeated is greedily searching for any part of the expression that matches f_[most__, last_] so that

c[a1 + a2, a3 + a4, a5] //. f_[most__, last_] :> f[most][last]

yields

c[a1[a2]][a3[a4]][a5]

Restricting the pattern to c[most__, last_] instead of f_[most__, last_] remedies that

pseudocurry[expr_] := expr //. Head[expr][most__, last_] :> Head[expr][most][last]

c[a1 + a2, a3 + a4, a5] // pseudocurry
c[a1 + a2][a3 + a4][a5]
$\endgroup$
  • 1
    $\begingroup$ Unfortunately this hits the problem @Mr.Wizard identified: c[a1 + a2, a3 + a4, a5] yields c[a1[a2]][a3[a4]][a5] under that scheme. $\endgroup$ – b3m2a1 Dec 27 '16 at 9:14
  • 1
    $\begingroup$ @MB1965 thanks for the comment, see improved answer $\endgroup$ – Sascha Dec 27 '16 at 9:32
4
$\begingroup$

Another old function Compose (superseeded by Composition but does some stuff that its supersessor doesn't):

pseudoCurry = Fold[Compose, #[[0]], #]&;

pseudoCurry @ c[a1, a2, a3, a4, a5]

c[a1][a2][a3][a4][a5]

pseudoCurry @ c[a1, {a2, a3}, a4 + a5]

c[a1][{a2, a3}][a4 + a5]

$\endgroup$
  • $\begingroup$ ... Fold[HeadCompose, #[[0]], #]& gives the same result. $\endgroup$ – kglr Dec 31 '16 at 11:29

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.