Remembering Previously Evaluated Function Values with Optional Argument

I am trying to implement a "Function That Remembers Values It Has Found" (name adjusted from the similarly named Mathematica guide (clicky here)). I have an optional argument and I believe that is giving me some issues, I know that it would be easy if I just gave the value of the optional argument when attempting to recall the function, but that seems to defeat the point of such a mechanism.

Here is an example of my problem (function anonymised for privacy reasons, it is not a function specific issue):

In[1]:= myFun[d_,m_,method_String,degrees_:False]:=
myFun[d,m,method,degrees]=foo[d,m,method,degrees];


If I run it and time it without the optional argument specified, it does not save the result.

In[2]:= Timing[myFun[2,6,"myMethod"];]
Out[2]= {1.73822, Null}

In[3]:= Timing[myFun[2,6,"myMethod"];]
Out[3]= {1.74781, Null}


So I checked with ? myFun and saw that it had indeed been saved with the optional argument specified, so gave that a try:

In[4]:= Timing[myFun[2,6,"myMethod",False];]
Out[4]= {0.000102, Null}


Giving what would have been expected. Is there any way so that I do not have to include my optional argument, it would be a shame if it were no longer optional!

You can name the user-call with a pattern and use that for memoization:

call : myFun2[x_, y_: 23] := call = (Pause[1]; x + y);

call : myFun3[x_, y_: 23] := call = myFun3[x, y] = (Pause[1]; x + y);


The first memoizes just the call:

AbsoluteTiming[myFun2[3]]
(*  {1.00087, 26}  *)

AbsoluteTiming[myFun2[3]]
(*  {1.*10^-6, 26}  *)

AbsoluteTiming[myFun2[3, 23]]  (* not memoized *)
(*  {1.00064, 26}  *)


The second memoizes the call, if the call was of the form myFun3[x], and it will memoize the call with the default, myFun3[x, 23], too:

AbsoluteTiming[myFun3[3]]
(*  {1.00028, 26}  *)

AbsoluteTiming[myFun3[3]]
(*  {1.*10^-6, 26}  *)

AbsoluteTiming[myFun3[3, 23]]
(*  {1.*10^-6, 26}  *)


The double assigment call = myFun3[x, y] =... is innocuously redundant if the call is of the form myFun3[x, y].

There might be more elegant ways to handle this, but a very simple one is to use an extra definition. That means instead of:

f[x_, n_: 2] := (f[x, n] = (Pause[1]; x^n))


(note that it usually is a good idea to include a minimal working example in your questions so that people who answer have something to start with...)

use this:

ClearAll@f
f[x_] := f[x, 2]
f[x_, n_] := (f[x, n] = (Pause[1]; x^n))

• Beat me to it :) +1. Commented Nov 10, 2018 at 21:02
• Thank you for this, I had been trying to include a MWE, but couldn't think of a function simple enough that would still take a while to evaluate, had completely forgotten about Pause. This solution is very good, but I feel it is rather disappointing that Mathematica cannot handle the optional argument here without additional prompting. Is there a reason for this that I am missing? Commented Nov 10, 2018 at 21:07
• @DanielWilson-Nunn: as you can see from the other answer this can be handled with only one definition. Which of these solutions is preferable will depend on various details of the problem and personal preferences. You would also have to try which variant is faster if that is an issue (I tend to always try performance claims with the actual problem and never guess...) Commented Nov 12, 2018 at 20:54