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$
8
  • $\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, 2016 at 2:51
  • $\begingroup$ Just use a conditional downvalue with recursion or Nest $\endgroup$
    – M.R.
    Dec 27, 2016 at 2:54
  • $\begingroup$ @Mr.Wizard exactly. For simplicity assume it's inert. $\endgroup$
    – b3m2a1
    Dec 27, 2016 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, 2016 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, 2016 at 9:18

5 Answers 5

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$
1
  • 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, 2016 at 3:01
18
$\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$
7
  • $\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, 2016 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$ Dec 27, 2016 at 3:09
  • 2
    $\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, 2016 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, 2016 at 3:22
  • 3
    $\begingroup$ ?? HeadCompose still gives the basic usage. $\endgroup$
    – Bob Hanlon
    Dec 27, 2016 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$
2
  • 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, 2016 at 9:14
  • 1
    $\begingroup$ @MB1965 thanks for the comment, see improved answer $\endgroup$
    – Sascha
    Dec 27, 2016 at 9:32
5
$\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$
1
  • 1
    $\begingroup$ ... Fold[HeadCompose, #[[0]], #]& gives the same result. $\endgroup$
    – kglr
    Dec 31, 2016 at 11:29
2
$\begingroup$

This is an example from the documentation of Construct (2018):

Fold[Construct, Head@#, List @@ #] &@c[a1, a2, a3, a4, a5]

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

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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