How to “cache” function values to pass to ListPlot?

I have a function which is very expensive to run. I would like to Plot the function for a certain range, but it is time prohibitive to let Plot simply compute the function for a lot of values as it sees fit.

Instead, I am calling the function for a range of values, and then calling ListPlot with the {x,y} pairs produced. However, I would like to refine the Plot by calling the function more times for areas of interest and then updating the ListPlot. I am unsure what the best way to do this is, or in fact how to do it at all.

My first thought was to create a SparseArray, and then I could fill in more values as they are calculated. But SpareArray requires an integer index, and my x values are real. I also can't use memoization because I need a quick and easy way to spit out all the {x,y} values I have computed so far, and I don't think there's an easy way to get this from a list of manual f[x]=y assignments.

Why not just add to your list of data as needed?

For example

f[x_] := Sin[x]
data = Table[{x, f[x]}, {x, 1, 10, .5}];
ListPlot[data] Then if you decide you want more values between, say, 7 and 9:

data2 = Table[{x, f[x]}, {x, 7, 9, .1}];
data3 = Join[data, data2];
ListPlot[data3] Edited per @BobHanlon's comment: In case you want to join the points, they should be sorted according to the x-values:

data3 = SortBy[Join[data, data2], First];
ListPlot[Sort[data3], Joined -> True, PlotMarkers -> Automatic] • Recommend that you use data3 = SortBy[Join[data, data2], First]; You could then use ListLinePlot or the option Joined -> True with ListPlot. – Bob Hanlon May 16 at 1:49
• @BobHanlon Yes, thanks. – MelaGo May 16 at 2:32

How about controlling Plot?

Plot[Sin[x], {x, 0, 4 Pi}, PlotPoints -> 10, MaxRecursion -> 2,
Mesh -> All, MeshStyle -> Red] There is a lot of flexibility to this. PlotPoints tells Plot how many points to start with. MaxRecursion tells Plot how many times it can improve the density. With each recursion it adds points to regions of the highest curvature.

Plot[Sin[x], {x, 0, 4 Pi}, PlotPoints -> 10, MaxRecursion -> 3,
Mesh -> All, MeshStyle -> Red] Plot[Sin[x], {x, 0, 4 Pi}, MaxRecursion -> 1, Mesh -> All,
MeshStyle -> Red] First, create an empty Association:

assoc = Association[{}]

Then, create a wrapper function to compute a new value and add it to the association. In this case f is the wrapper, and g is the original.

f[z_] := Module[{tmp},
tmp = g[z];
assoc = Append[assoc, z -> tmp];
]

Now all the values computed so far can be plotted using:

ListPlot[Map[Function[i, {i, assoc[i]}], Keys[assoc]]]

I would suggest using memoization in this situation. It's not as difficult as you say to get all the computed values out, see below. In this way you're sure never to lose any computed values, even the ones you add by manual assignments.

Here's an example. First, the memoizing function definition: make sure that the left-hand-side pattern contains a constraint such as ?NumericQ so that no symbolic evaluation will happen later on,

f[x_?NumericQ] := f[x] = N[Sin[x]]

To get all the already computed values, take the Downvalues and keep only those that have numerical input and output values: (here it's important that f is defined with a pattern constraint)

fvalues := Cases[DownValues[f],
RuleDelayed[_[f[x_]], y_] /; NumericQ[x] && NumericQ[y] -> {x, y}]

Usage example: start with an overview plot, using @DavidKeith 's method (but works with any method, including your original suggestion),

Plot[f[x], {x, 0, 4 π}, PlotPoints -> 10, MaxRecursion -> 2,
Mesh -> All, MeshStyle -> Red] zoomed-in plot:

Plot[f[x], {x, π/2, π}, PlotPoints -> 10, MaxRecursion -> 2,
Mesh -> All, MeshStyle -> Red] Plot all results computed so far:

ListPlot[fvalues] You can then use Save["f.wl", f] to save all computed values for a later session.