15

Suppose we're given the expression f*g+h, where f,g,h are all pure functions. How can we evaluate this expression on some x? If there were only one operation, say f+g, we could simply use Through[(f+g)[x]], but Through only deals with one operation, as far as I can see. How is this done?

Kuba
  • 136,707
  • 13
  • 279
  • 740
Nilay Kumar
  • 253
  • 1
  • 6
  • To clarify, you are asking for through to apply the pure functions at the most nested level to x? Your example is Plus[Times[f,g],h], so the functions you want applied to x are Level[f*g + h, {-1}]? – VF1 Jul 04 '13 at 06:44
  • Apply[(f[##] g[##] + h[##]) &, {arguments}], crude version of what Mr. Wizard have showed. – Kuba Jul 04 '13 at 06:57
  • Ah, yes, that is what I want @VF1 – Nilay Kumar Jul 04 '13 at 07:03

3 Answers3

13

Edit:

Mr.Wizard helped to refine my old function to:

SetAttributes[Through2, HoldFirst]
Through2[head_[args___]] := Replace[head, s : _Function | _Symbol :> s[args], -1]

This locates the most nested functions and symbols and evaluates their value for the parameter arguments.

Below is my older, less robust function:

SetAttributes[Through2, HoldFirst]
Through2[expr_] := 
 With[{head = Head@expr, arg = First@expr}, 
  With[{funcs = Cases[head, _Function | _Symbol, -1]}, 
   head /. Thread[funcs -> Through[funcs[arg]]]]]
Through2[(f*g + h)[x]]
(* f[x] g[x] + h[x] *)
Through2[(f*g + (h*Minus)^2)[x]]
(* f[x] g[x] + x^2 h[x]^2 *)
Through2[{Re, Im + Re}[x]]
(* {Re[x], Im[x] + Re[x]} *)
VF1
  • 4,702
  • 23
  • 31
7

Perhaps you want something like this?

apply = (# /. s_Symbol /; Context[s] =!= "System`" :> s[##2]) &;

apply[f*g + h, x]
f[x] g[x] + h[x]

This is a limited implementation but it can be extended if this is in fact the kind of operation you desire. The idea is to recognize any Symbol not belonging to the System` context as a function to apply to x. Alternatively one could apply only symbols in the Global` context using Context[s] === "Global`".

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • Yes, I'd say that our functions do the same thing (with yours being more elegant and direct). In hindsight, Cases and then ReplaceAll does seem a bit redundant - and dangerous, too, if one of the pure functions is used on a higher level in the expression. Your solution will be more robust. – VF1 Jul 04 '13 at 07:13
  • @VF1 Thanks for the check. If you'd like to put this in your answer instead that's fine with me; it was your idea to look at the levelspec. – Mr.Wizard Jul 04 '13 at 07:22
  • Will do. Thanks. – VF1 Jul 04 '13 at 07:24
  • @VF1 It just occurred to me that my {-1} code is broken for Function as that will not be found at level {-1}; Please use the Heads -> False code, assuming that isn't broken. – Mr.Wizard Jul 04 '13 at 07:26
  • Is there a difference between Yours apply and @VF1 's Through2 exept that the latter works for 1 argument only (after edit it works for many) and Yours can not handle Minus and other System context functions? – Kuba Jul 04 '13 at 07:29
  • Heads->False didn't work either on examples such as Through2[{#1 &, Im}[x]], unfortunately, as Function is a head. However, using the "relative" level of -1 instead of {-1} seems to be a fix. – VF1 Jul 04 '13 at 07:32
  • @Kuba Indeed, but as Mr.Wizard mentions in his answer, the context can be changed. – VF1 Jul 04 '13 at 07:45
  • @VF1 "Indeed" implies "there is a difference" other than those I've mentioned. Sorry for catching the words but I want to be sure :) – Kuba Jul 04 '13 at 07:50
  • @VF1 I was not thinking very well; of course Heads -> False is the default for Replace. By the way I meant use the version with -1 rather than {-1}, just as you did. I'm now deleting that section of my answer. – Mr.Wizard Jul 04 '13 at 10:03
  • @Kuba Referring to the code presently in both answers, they are entirely different approaches. Mine works based on the idea that active functions can be referenced by context, whereas VF1's method works by recognizing any Symbol (or Function) that does not itself have arguments. Arguably both are useful. – Mr.Wizard Jul 04 '13 at 10:07
  • @Mr.Wizard I have asked a vague question, I know that aproaches differ. I meant, are there others differences in effect/application of those two? – Kuba Jul 04 '13 at 10:10
0

For example...

f=#&;
g=2#^2&;
h=-Sqrt[#]&;

Using substitution rule...

f*g+h /. z:(f|g|h)->z[3]

Gives...

54-Sqrt[3]
Ymareth
  • 4,741
  • 20
  • 28