Mathematica:如何清除符号的缓存,即取消模式无关DownValues

24

我是一个糟糕的缓存器:有时候,在没有人看的情况下,我会像这样缓存结果,而不包括完整的上下文:

f[x_]:=f[x]=x+a;
a=2; f[1];
DownValues[f]

Out[2]= {HoldPattern[f[1]]:>3,HoldPattern[f[x_]]:>(f[x]=x+a)}
这会导致非常微妙的错误,并且更重要的是,需要在更改上下文时清除缓存。一种清除缓存的方法是完全 Clear 符号并重复定义,但这并不是真正的解决方法。
我真正想要的是一种清除与符号相关的所有无模式 DownValues 的方法。
为了明确起见,我将在答案中包含我的现有解决方案,但它有两个问题:
- 它只清除所有数字参数的 DownValues。 - 出于美观原因,我想避免使用 Block 来获取 DownValues。
有什么想法来改进 ClearCache 吗?
4个回答

25

我以前写过类似的函数(但我不记得在哪里写过了)。

以下代码是否满足你的所有需求?

ClearCache[f_] := DownValues[f] = DeleteCases[DownValues[f], 
                                              _?(FreeQ[First[#], Pattern] &)]

这可能需要扩展到 UpValuesSubValues。并且 fHead 限制为 Symbol


@belisarius:你的评论没有点赞让我感到很焦虑,害怕自己做了什么愚蠢的事情... 简单的解决方案并不代表我就是个傻瓜,对吧? - Simon
@Simon 我刚忘记点赞了,真是太丢人了!那个模式真的很棒。 - Dr. belisarius
哇!这看起来就像是我想要做的事情。只有一个小技术细节需要让问题保持开放几分钟:即使只有右手边有模式,你也会取消一个下值的清除资格 - 对于ClearCache Nirvana,我们可能只需要关注左手边。 - Janus
@Janus:幸运的是,这很容易解决——请参见编辑。(顺便说一句;涅槃?你一定非常想要这个“ClearCache”函数!) - Simon
3
谢谢,Simon!我会再开放几个小时——因为当我还没看到问题时就被关闭总是让我很烦恼 :) - Janus

8

补充其他优秀解决方案:如果你有一个非常大的DownValues列表,并且对于ClearCache有严格的效率要求,那么你可以通过清除所有定义,然后仅重构具有模式的定义来显著加速该过程。以下是一个例子:

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)}

请注意,您只需要使用创建基于模式的函数定义的代码一次调用createDefs。所有其他时间,您将其作为createDefs[f]调用,因为它会在第一次调用时记忆需要重新创建定义的代码。
另外,您可能不想增加巨大的缓存,但是在简单的f[x_]:=f[x]=rhs方法中,这是不可控制的。换句话说,缓存可能包含许多不必要的旧内容,但在此方法中,您无法区分旧的(不再使用)定义和新的定义。我使用一个名为Cache的包部分解决了这个问题,可以在这里找到它以及说明其使用的笔记本。它使您对缓存的大小有更多控制。它也有它的问题,但偶尔可能会有用。

谢谢,Leonid。很高兴在这里列出这种方法。我偶尔使用它(即在构建“ClearCache”之前),但我不太喜欢它干扰实现过程的方式。对于我的特定用途,我缓存的结果是如此昂贵,以至于我可以在眨眼间清除一年的缓存 :) - Janus

7
一次我实现了一个方案来限制记忆化值的数量(并节省内存)。在该页面上搜索记忆化。这也可能对此处有用(特别是考虑到一些标记为此问题的重复问题)。 代码
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] =. )

使用和描述

此版本适用于接受单个数字参数的函数。调用memo[f][x]而不是f[x]来使用记忆化版本。缓存的值仍与f相关联,因此当f被清除时,它们会消失。默认情况下,缓存值的数量限制为200。使用memoClearCache[f]清除所有记忆化值。


3
这是我目前解决问题的方案,但正如问题中提到的那样,它并不严格寻找无模式DownValues,也不太优雅。
请存储fDownValues
In[6]:= dv = DownValues[f]

Out[6]= {HoldPattern[f[1]] :> 3, HoldPattern[f[x_]] :> (f[x] = x + a)}

Block中查找需要清除的DownValues以避免立即求值。

In[7]:= dv2clear = Block[{f},
  Hold@Evaluate@Cases[dv,
      HoldPattern[f[args__ /; Apply[And, NumericQ /@ Flatten[{args}]]]], {3}]]

Out[7]= Hold[{f[1]}]

Unset应用于保持的列表中的目标DownValues,然后释放。

In[8]:= Map[Unset, dv2clear, {2}]
ReleaseHold@%

Out[8]= Hold[{(f[1]) =.}]

这个很好用

In[10]:= DownValues[f]

Out[10]= {HoldPattern[f[x_]] :> (f[x] = x + a)}

可以这样包装起来:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接