6
$\begingroup$

For given functions $f,g,\cdots$ I want to define a pure function that, when applied to any list $\{x,y,\cdots\}$ (of the same length as $\{f,g,\cdots\}$), produces $\{f[x],g[y],\cdots\}$. Two ways to achieve this are

{f@#[[1]],g@#[[2]],...} &

and

MapThread[
   #1[#2] & (* or Apply[#1,{#2}] & *),
   {{f,g,...},#}
] &

With all of Mathematica's built in functions, however, I would be surprised if there does not exist a slicker way to do this. Thus my question is: is there a more elegant way to do the above?

For concreteness let's take just to functions $\{f,g\}$ and apply it to $\{x,y\}$, yet I'm hoping for an answer that generalizes to more than two functions.

Although I would expect that this has been asked here before I cannot find it.

$\endgroup$
  • 1
    $\begingroup$ My first approach would have been the MapThread you show here. $\endgroup$ – Szabolcs Nov 2 '16 at 12:57
  • $\begingroup$ Yeah, I feel like this is one of the things that MapThread was designed for. $\endgroup$ – march Nov 2 '16 at 15:42
  • 3
    $\begingroup$ For the first argument of MapThread, you could use Compose rather than a pure function: MapThread[Compose, {{f, g, ...}, #}] &. $\endgroup$ – user31159 Nov 2 '16 at 19:15
  • $\begingroup$ @xavier: Thanks, that's nice indeed! If you turn your suggestion into an answer I'll accept it $\endgroup$ – Jules Lamers Nov 3 '16 at 10:27
  • $\begingroup$ @JulesLamers Please feel free to make it an answer if it satisfies you. (It is also OK to accept one's own answer.) $\endgroup$ – user31159 Nov 3 '16 at 18:13
5
$\begingroup$

Let lst1 be the list of heads of your functions:

lst1={f,g,h};

for example they may be Cos, Sin, Exp and so on, and lst2 is the list of numbers to which you want to apply the heads:

lst2={a,b,c};

A most simple way seems me to be this:

    Transpose[{lst1, lst2}] /. {x_, y_} -> x[y]

(*   {f[a], g[b], h[c]}  *)

Another way would be to first introduce a function:

   s[x_, y_] := x[y]

and with its use do this:

MapThread[s, {lst1, lst2}]

(*  {f[a], g[b], h[c]}  *)

or this:

Inner[s, lst1, lst2, List]

{f[a], g[b], h[c]}

One can probably find something else, but in the moment I do not see.

Have fun!

$\endgroup$
2
$\begingroup$

I think the MapThread approach is already elegant. Anyway, the following is the shortest solution so far:

#@#2 & @@@ ArcTan[{f, g}, #] &
$\endgroup$
  • $\begingroup$ Do you have a specific reason for using ArcTan? $\endgroup$ – Jules Lamers Nov 2 '16 at 12:54
  • 2
    $\begingroup$ @JulesLamers No, if you use List, then you only got {f[g], x[y]}. I use ArcTan because it owns Listable attribute and seems not to evaluate to other things even if its 2nd argument is some special number e.g. ArcTan[f, 0] still outputs ArcTan[f, 0]. $\endgroup$ – xzczd Nov 2 '16 at 12:56
  • $\begingroup$ (You're right, my suggestion to use List instead of ArcTan was incorrect; I edited my comment accordingly) $\endgroup$ – Jules Lamers Nov 2 '16 at 13:00
2
$\begingroup$

One can use the Listable attribute to apply a list of function to lists of arguments. One can extend it easily to multiple lists of arguments (see last example).

Either via Function:

Function[{x, y}, x[y], Listable][{f, g, h}, {x, y, z}]
(*  {f[x], g[y], h[z]}  *)

Or via a symbol:

Block[{threadApply},
 SetAttributes[threadApply, Listable];
 threadApply[f_, x___] := f[x];
 threadApply[{f, g, h}, {x, y, z}]
 ]
(*  {f[x], g[y], h[z]}  *)

With the symbolic form, it's easy to deal with an arbitrary number of arguments:

Block[{threadApply},
 SetAttributes[threadApply, Listable];
 threadApply[f_, x___] := f[x];
 threadApply[{f, g, h}, {a, b, c}, {x, y, z}]
 ]
(*  {f[a, x], g[b, y], h[c, z]}  *)

Block[{threadApply},
 SetAttributes[threadApply, Listable];
 threadApply[f_, x___] := f[x];
 threadApply[{f, g, h}]
 ]
(*  {f[], g[], h[]}  *)

This is also possible with a slight modification of the OP's original MapThread:

MapThread[#1[##2] &, {{f, g, h}, {a, b, c}, {x, y, z}}]
(*  {f[a, x], g[b, y], h[c, z]}  *)
$\endgroup$
  • $\begingroup$ Cool, thanks for your answer! It's nice to see the versions with several arguments as well. Just a small question about the final version: why do you prefer #1[##2]& over Compose? $\endgroup$ – Jules Lamers Dec 12 '16 at 12:28
  • 1
    $\begingroup$ @JulesLamers You're welcome! Note that Compose[f, a, x] and #1[##2]&[f, a, x] are not the same. There's also this warning: reference.wolfram.com/language/ref/Compose.html $\endgroup$ – Michael E2 Dec 12 '16 at 13:15
  • $\begingroup$ As for the first of course you're quite right, I was thinking about the two-argument case. Regarding the latter I did see that warning, but since Compose and Composition are different I'm not sure what to do with it; am I supposed not to use Compose` any more? $\endgroup$ – Jules Lamers Dec 12 '16 at 13:25
  • 1
    $\begingroup$ @JulesLamers I suppose Compose is not going away (since it hasn't after nine version updates). I'll probably use #1[#2]& or Composition with # and & depending on the case. It's not a big deal to me, but I'd rather not have my code dependent on it. Even if it were to go away, it's easy enough to add a definition. $\endgroup$ – Michael E2 Dec 12 '16 at 13:37
1
$\begingroup$

Encouraged by the comments I think that the following slight improvement, suggested by xavier (user31159), of the second attempt I included in my question is (at least quite close to) what I was hoping for:

MapThread[
  Compose,
  {{f,g,...},#}
] &

Thanks for the other suggestions as well!

$\endgroup$
  • 2
    $\begingroup$ Altho Compose[] is obsolete; it's quite nice. Anyway: Inner[Compose, lst1, lst2, List], if you want something slot-free. $\endgroup$ – J. M. will be back soon Dec 12 '16 at 10:49

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.