I'm a bad cacher: Sometimes, when no one is watching, I'll cache results without including the full context like so:
f[x_]:=f[x]=x+a;
a=2; f[1];
DownValues[f]
Out[2]= {HoldPattern[f[1]]:>3,HoldP开发者_C百科attern[f[x_]]:>(f[x]=x+a)}
This leads to horribly subtle bugs, and, more importantly, to the need for clearing the cache when I change the context. One way of clearing the cache is to completely Clear
the symbol and repeat the definitions, but this is not really a solution.
What I would really like is a method for clearing all pattern-free DownValues associated with a symbol.
For clarity, I'll include my present solution as an answer, but if fails on two counts- It only clears DownValues with all-numeric arguments
- For aesthetical reasons, I'd like to avoid using
Block
to grab the DownValues.
Any ideas on how to improve ClearCache
?
I've made similar functions in the past (but I can't remember where).
Does the following code do all that you need?
ClearCache[f_] := DownValues[f] = DeleteCases[DownValues[f],
_?(FreeQ[First[#], Pattern] &)]
This maybe should be extended to UpValues
and SubValues
. And the Head
of f
restricted to Symbol
.
Just to complement the other excellent solution: if you have a very large list of DownValues
and have strict efficiency requirements for ClearCache
, you can significantly speed up the process by clearing all definitions and then reconstructing only those with patterns. Here is an example:
In[1]:=
ClearCache[f_] :=
DownValues[f] = DeleteCases[DownValues[f], _?(FreeQ[First[#], Pattern] &)];
In[2]:= Clear[f];
f[x_] := f[x] = x;
In[4]:= f /@ Range[1000000];
In[5]:= ClearCache[f]; // Timing
Out[5]= {7.765, Null}
In[6]:=
ClearAll[createDefs];
SetAttributes[createDefs, HoldRest];
createDefs[f_, defs_: Automatic] :=
(createDefs[f] := (Clear[f]; defs); createDefs[f]);
In[9]:= Clear[f];
createDefs[f, f[x_] := f[x] = x]
In[11]:= f /@ Range[1000000];
In[12]:= Length[DownValues[f]]
Out[12]= 1000001
In[13]:= createDefs[f]; // Timing
Out[13]= {1.079, Null}
In[14]:= DownValues[f]
Out[14]= {HoldPattern[f[x_]] :> (f[x] = x)}
Note that you only have to call the createDefs
once with the code that creates the pattern-based definitions of the function. All other times, you call it as createDefs[f]
, because it memoizes the code needed to re-create the definitions, on the first call.
It is also possible that you don't want to grow huge caches, but this is out of your control in the simple f[x_]:=f[x]=rhs
approach. In other words, the cache may contain lots of unnecessary old stuff, but in this approach you can not tell old (no longer used) definitions from the new ones. I partially addressed this problem with a package I called Cache, which can be found here together with the notebook illustrating its use. It gives you more control over the size of the cache. It has its problems, but may occasionally be useful.
Once I implemented a scheme to limit the number of memoized values (and conserve memory). Search for memoization on that page. This might be useful here as well (especially considering some of the questions marked as duplicate of this one).
The code
SetAttributes[memo, HoldAll]
SetAttributes[memoStore, HoldFirst]
SetAttributes[memoVals, HoldFirst]
memoVals[_] = {};
memoStore[f_, x_] :=
With[{vals = memoVals[f]},
If[Length[vals] > 200,
f /: memoStore[f, First[vals]] =.;
memoVals[f] ^= Append[Rest[memoVals[f]], x],
memoVals[f] ^= Append[memoVals[f], x]];
f /: memoStore[f, x] = f[x]]
memo[f_Symbol][x_?NumericQ] := memoStore[f, x]
memoClearCache[f_Symbol] :=
(Scan[(f /: memoStore[f, #] =.) &, memoVals[f]];
f /: memoVals[f] =. )
Usage and description
This version works with functions that take a single numerical argument. Call memo[f][x]
instead of f[x]
to use a memoized version. Cached values are still associated with f
, so when f
is cleared, they are gone. The number of cached values is limited to 200 by default. Use memoClearCache[f]
to clear all memoized values.
This is my present solution to the problem, but as mentioned in the question is doesn't strictly look for pattern-free DownValues
, nor is it very elegant.
Store the DownValues
for f
In[6]:= dv = DownValues[f]
Out[6]= {HoldPattern[f[1]] :> 3, HoldPattern[f[x_]] :> (f[x] = x + a)}
Find the DownValues
to clear inside a Block
to avoid immediate evaluation
In[7]:= dv2clear = Block[{f},
Hold@Evaluate@Cases[dv,
HoldPattern[f[args__ /; Apply[And, NumericQ /@ Flatten[{args}]]]], {3}]]
Out[7]= Hold[{f[1]}]
Apply Unset
to the targeted DownValues
inside the held list and then release
In[8]:= Map[Unset, dv2clear, {2}]
ReleaseHold@%
Out[8]= Hold[{(f[1]) =.}]
This works fine
In[10]:= DownValues[f]
Out[10]= {HoldPattern[f[x_]] :> (f[x] = x + a)}
And can be wrapped up like so:
ClearCache[f_] := Module[{dv, dv2clear},
(* Cache downvalues for use inside block *)
dv = DownValues[f];
(* Find the downvalues to clear in Block to avoid immediate evaluation *)
dv2clear = Block[{f},Hold@Evaluate@Cases[dv,HoldPattern[
f[args__ /; Apply[And, NumericQ /@ Flatten[{args}]]]], {3}]];
(* Apply Unset to the terms inside the held list and then release *)
ReleaseHold@Map[Unset, dv2clear, {2}];]
精彩评论