9

If I have a function, that returns a function, for example:

f[a_] := a^2 * # &

then

f[3] == 3^2 # &

Is there a way to tell mathematica that I want part of the function evaluated, so that

f[3] == 9 # &

Obviously this example is quite simple, but in general the evaluation may be slow, and better if it's only performed once. I tried wrapping the important part (in this case, a^2) in 'Evaluate', but that didn't help.

Thanks!

Tom
  • 153
  • 4

2 Answers2

12

The general way to construct functions with partially evaluated pieces is to use With, which is a general device for injecting evaluated pieces in otherwise unevaluated or held expressions. In your case, it would look like

f[a_] := With[{sqa = a^2}, sqa * # &]

The method based on Evaluate is generally less powerful, since evaluation of the entire body may be not desirable, while more deeply nested Evaluate will not result in what you want, due to the (local) way of how Evaluate works (because Function does not evaluate its body at the time it is constructed, the evaluation process, associated with the construction of the function, simply won't come to more deeply nested Evaluate instructions).

Leonid Shifrin
  • 114,335
  • 15
  • 329
  • 420
  • Perfect! Thank you! – Tom Feb 08 '14 at 19:09
  • 1
    I thought the OP wants to cache the result of f[3] such that f[3] can be run many times without calculating a^2 again and again (though he is happy with this answer). In this case, a single With or Evaluate doesn't help. Caching is also needed: f[a_] := f[a] = With[{x = a^2}, x*# &]. One can use Trace to see the difference. – Yi Wang Feb 08 '14 at 19:46
  • @YiWang Quite frankly, I don't see how the OP's question can have a memoizing interpretation, given the exact wording of the question. I think the OP has been very clear about what was being asked, and the topic of how the resulting closure is stored, if it needs to (in a separate variable, or as a cached value) is a separate one. – Leonid Shifrin Feb 08 '14 at 20:10
  • 1
    With your answer, if I run f[3] // Trace, the result is {f[3],With[{sqa=3^2},sqa #1&],{3^2,9},9 #1&}. I guess the OP does not only want to run f[3] and get 9 # &, but also f[3] is only calculated once. This is what I can read from his sentence "Obviously this example is quite simple, but in general the evaluation may be slow, and better if it's only performed once. " I didn't see how With[...] can speed up things here without memorization. – Yi Wang Feb 08 '14 at 20:23
  • 1
    Well, I think there are two cases depending on how to use f[3] with the calculation "only performed once". One way is let, say, g=f[3], and use g many times. Another is directly use f[3] many times. In the former case you are right, and your answer fully fills the OP's need. In the latter case, memorization is also needed. I don't know which is the exact case here... – Yi Wang Feb 08 '14 at 20:30
  • 1
    @YiWang No need to explain to me why f[3] recomputes every time with my method, I don't disagree with you on that. My point is that, from where I stand, the OP did not ask for the f[n] calls optimization, and his problem was how to inject evaluated parts into otherwise unevaluated body of the function. In my experience, it is often better to not make additional assumptions, w.r.t. those made by the OP. Particularly in this case, where the question has been formulated quite clearly. If the OP wants memoization, there is plenty of information on it on this site already. – Leonid Shifrin Feb 08 '14 at 20:35
  • 1
    @LeonidShifrin For what it's worth, I understood the question the same way as Yi Wang, i.e. define f so that 3^2 is not computed each time in f[3] (but not necessarily via memoization). I don't disagree with your answer though. – rm -rf Feb 09 '14 at 03:13
  • @rm-rf Memoization has a price, which is, state is introduced. Pure functions are typically passed to other (higher-order) functions. So, memoization of pure functions is, to my mind, something reserved for special circumstances, like e.g. Compile. You could argue that this case is similar, but it is hard to determine that without having a larger context. For example, in cases like Select[some-list, f[3]], memoization is not needed, while my answer still provides the necessary optimization - and I'd think the majority of typical use cases are like that - a one-off function construction. – Leonid Shifrin Feb 09 '14 at 14:56
  • @rm-rf [1] To add a bit more to it, memoization for named functions is reasonably safe because it is assumed that the user performs the name management. Pure functions are, however, typically constructed for a single use. The whole point of anonymous functions is that we want to not give a function a name, presumably because we want to avoid the name management problem for cases where it is not needed. By introducing the memoization for pure function's constructor, we effectively start to name them again, so the main purpose of an anonymous function is defeated. I am not saying that one should – Leonid Shifrin Feb 09 '14 at 15:00
  • @rm-rf [2] never do that, but that IMO we should not make this a default, both because we introduce state, and because we destroy some of the main advantages of pure functions (SubValues could be used just as well then, more or less). There are hidden costs one pays for memoization, and when the memoized function is a function generator, these costs are different than when they are just some expressions, because the use of such memoized function is different. – Leonid Shifrin Feb 09 '14 at 15:04
4

Leonid already named the big one, With, but there are a few more approaches I'd like to outline. First of all you can use Evaluate if you wish to evaluate the entire body of the Function, and if the Function isn't part of some larger held expression. I fully agree with Leonid however that With is more general and less prone to surprises here. Nevertheless for reference:

f[a_] := Evaluate[a^2 #] &
f[7]
49 #1 &

Likewise you can Apply Function to a List (or any other inert Head without a hold attribute):

f[a_] := Function @@ {a^2 #}
f[7]
49 #1 &

For spot evaluation in a deeper expression (like With) you can use a replacement rule in an inverted fashion. (See this Q&A for more examples.)

f[a_] := a^2 /. x_ :> (x # + 2 + 2 &)
f[7]
49 #1 + 2 + 2 &

Note that 2 + 2 is not evaluated. Also note the placement of ( ) to group the right-hand-side of RuleDelayed, because & has low operator precedence.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371