5
$\begingroup$

I'm getting completely stumped on writing a function makeIterate that constructs an iterated structure out of a list of functions e.g. {f, g, h} and a list of corresponding arguments {d1, d2, d3} (same number of arguments). More specifically, I would like the input

makeIterate[expr, {f, g, h}, {d1, d2, d3}, final]

have the following output

f[expr, {d1}, g[#, {d2}, h[#, {d3}, final] &] &]

The first function acts on expr, taking d1 as the second argument. Each subsequent function is to be inserted into the third argument.

The problem I'm running into here is that since the functions {f, g, h} themselves need to end up being inside a Function in the output, I'm having trouble disentangling the pure Functions that are supposed to be part of the output, and the Functions that are meant to be used in makeIterate.

Any suggestions?

$\endgroup$

1 Answer 1

4
$\begingroup$

Here is one way:

makeIterateFunction[fns_, data_, final_] :=
  Fold[
    Function@@{#2[[1]][Slot@@{1}, {#2[[2]]}, #]}&
  , final
  , Reverse@Transpose@{fns, data}
  ]

makeIterate[expr_, fns_, data_, final_] :=
  makeIterateFunction[fns, data, final][expr]

So then:

makeIterateFunction[{f, g, h}, {d1, d2, d3}, final]

(* f[#, {d1}, g[#, {d2}, h[#, {d3}, final] &] &] & *)

and

makeIterate[expr, {f, g, h}, {d1, d2, d3}, final]

(* f[expr, {d1}, g[#, {d2}, h[#, {d3}, final] &] &] *)

This assumes that the arguments to makeIterate do not need to be held. If this is not the case, then we must take more care to ensure that there are no evaluation leaks:

SetAttributes[{makeIterateFunction2, makeIterate2}, HoldAll]

makeIterateFunction2[fns_, data_, final_] :=
  Fold[
    Replace[{##}, {_[x_], _[f_, d_]} :> Hold[f[#, {d}, x]&]]&
  , Hold@final
  , Reverse@Thread@Hold[fns, data]
  ] // ReleaseHold

makeIterateFunction2[{f, g, h}, {d1, d2, d3}, final]

makeIterate2[expr_, fns_, data_, final_] :=
  makeIterateFunction2[fns, data, final][expr]

These functions give the same results as the originals except when the arguments are not inert. Using a nonsense example for illustration, compare what happens when the original function is given non-inert arguments:

makeIterateFunction[{f, g, Abort[]}, {d1, d2, Abort[]}, Abort[]]

(* $Aborted *)

with the behaviour the updated function in the same conditions:

makeIterateFunction2[{f, g, Abort[]}, {d1, d2, Abort[]}, Abort[]]

(* f[#,{d1},g[#,{d2},Abort[][#,{Abort[]},Abort[]]&]&]& *)

```
$\endgroup$
1
  • $\begingroup$ Ah, so the trick is to put in a List before switching it out for Function. That's how to avoid the dreaded Function clash $\endgroup$
    – QuantumDot
    Mar 5, 2021 at 1:12

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.