开发者

Mathematica: How to clear the cache for a symbol, i.e. Unset pattern-free DownValues

开发者 https://www.devze.com 2023-02-12 14:27 出处:网络
I\'m a bad cacher: Sometimes, when no one is watching, I\'ll cache results without including the full context like so:

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}];]
0

精彩评论

暂无评论...
验证码 换一张
取 消