当DownValues已经存在时,可以阻止OwnValues吗?

时间:2011-08-02 19:43:53

标签: wolfram-mathematica variable-assignment

对于已经指定与名称“a”关联的DownValues的情况,是否有可接受的方法来阻止将OwnValues分配为相同的名称? (我最初在玩某人尝试实现数据字典时遇到过这个问题。)

这就是我要避免的意思:

Remove[a];
a[1] := somethingDelayed
a[2] = somethingImmediate;
DownValues[a]
a[1]
a[2]

...返回

{HoldPattern[a[1]] :> somethingDelayed, 
 HoldPattern[a[2]] :> somethingImmediate}
somethingDelayed
somethingImmediate

现在,如果我们要评估:

a = somethingThatScrewsUpHeads;
(* OwnValues[a] above stored in OwnValues *)
a[1]
a[2]

我们得到......

somethingThatScrewsUpHeads[1]
somethingThatScrewsUpHeads[2]

是否有一种简单/灵活的方法可以防止DownValues中任何名称的OwnValues? (Lemme猜......这是可能的,但会有性能受损吗?)

2 个答案:

答案 0 :(得分:12)

我不知道这是否是“已接受”的方式,但您可以定义一条规则,阻止SetSetDelayeda采取行动:

Remove[a];
a[1] := somethingDelayed
a[2] = somethingImmediate;

a /: HoldPattern[(Set|SetDelayed)[a, _]] := (Message[a::readOnly]; Abort[])

a::readOnly = "The symbol 'a' cannot be assigned a value.";

有了这条规则,任何将OwnValue分配给a的尝试都将失败:

In[17]:= a = somethingThatScrewsUpHeads;

During evaluation of In[17]:= a::readOnly:
  The symbol 'a' cannot be assigned a value.

Out[17]= $Aborted

In[18]:= a := somethingThatScrewsUpHeads;

During evaluation of In[18]:= a::readOnly:
  The symbol 'a' cannot be assigned a value.

Out[18]= $Aborted

但是,此规则仍允许DownValues的新a

In[19]:= a[3] = now;
         a[4] := later

In[20]:= a[3]

Out[20]= now

In[21]:= a[4]

Out[21]= later

<强>性能

该规则似乎对SetSetDelayed的效果没有明显的影响,可能是因为规则是作为a的上限值安装的。我试图通过执行......来验证这一点。

Timing@Do[x = i, {i, 100000000}]

...安装规则之前和之后。时间上没有可观察到的变化。然后我尝试在10,000个生成的符号上安装Set - 相关的up-值,因此:

Do[
  With[{s=Unique["s"]}
  , s /: HoldPattern[(Set|SetDelayed)[s, _]] :=
      (Message[s::readOnly]; Abort[])
  ]
, {10000}]

同样,即使有如此多的高价值规则,时机也没有改变。这些结果表明,从性能的角度来看,这种技术是可以接受的,尽管我强烈建议您在特定应用的环境中进行性能测试。

答案 1 :(得分:9)

我不知道有什么方法可以直接“阻止”OwnValues,因为Mathematica的评估者先评估头部(部分,DownValuesUpValues和{{1等等,这并没有把我们带到任何地方(我简要地讨论了这个问题in my book)。

直接方法的问题在于它可能基于将SubValues添加到DownValuesSet,因为它看起来像they can not be overloaded via UpValues

修改

正如@WReach在评论中指出的那样,因为我们正在处理SetDelayed s UpValues / { Symbol,因此标记深度Set就足够了。我的评论与在某些头上重新定义SetDelayed更相关,并且必须允许将具有这些头部的表达式存储在变量中(例如1分配或由头部区分的自定义数据类型)

结束编辑

但是,在大多数情况下,为SetPart添加DownValues是灾难的处方(this线程非常具有说明性),应该使用非常很少(如果有的话)而且非常小心。

从不太极端的方法来看,也许最简单,最安全但不自动的方法是在定义它们之后Set符号。此方法存在一个问题,即如果没有SetDelayed符号,您将无法添加新的或修改现有定义。

或者,为了实现自动化,您可以使用许多技巧。一种是定义自定义赋值运算符,例如

Protect

并在Unprotect中始终包含ClearAll[def]; SetAttributes[def, HoldAll]; def[(op : (Set | SetDelayed))[lhs_, rhs_]] /; Head[Unevaluated[lhs]] =!= Symbol || DownValues[lhs] === {} := op[lhs, rhs] - 和SetDelayed分配(我为Set选择了此语法 - 保留def / {{1} } def - 保持语法高亮显示),Set相同。以下是您的示例的样子:

SetDelayed

然后你可以进一步编写一个代码处理宏,它将在代码的任何地方def中包含基于Set - 和In[26]:= Clear[a]; def[a[1]:=somethingDelayed]; def[a[2]=somethingImmediate]; def[a=somethingThatScrewsUpHeads]; In[30]:= {a[1],a[2]} Out[30]= {somethingDelayed,somethingImmediate} 的作业:

SetDelayed

因此,您可以将您的代码包装在Set中,然后根本不必更改那段代码。例如:

def

在交互式会话中,您可以更进一步并设置SetAttributes[useDef, HoldAll]; useDef[code_] := ReleaseHold[Hold[code] /. {x : (_Set | _SetDelayed) :> def[x]}] ,然后您不会忘记将代码包装在useDef中。

修改

通过使用模式匹配器向In[31]:= useDef[ Clear[a]; a[1]:=somethingDelayed; a[2]=somethingImmediate; a=somethingThatScrewsUpHeads; ] In[32]:= {a[1],a[2]} Out[32]= {somethingDelayed,somethingImmediate} 添加诊断功能是微不足道的。这是一个版本,如果尝试使用$Pre = useDef分配符号,将发出警告消息:

useDef

同样,通过使用def(可能使用DownValues),这可以是一个有效的调试工具,因为根本不需要更改原始代码。