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

tel*_*f14 9 wolfram-mathematica variable-assignment

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

这就是我要避免的意思:

Remove[a];
a[1] := somethingDelayed
a[2] = somethingImmediate;
DownValues[a]
a[1]
a[2]
Run Code Online (Sandbox Code Playgroud)

返回...

{HoldPattern[a[1]] :> somethingDelayed, 
 HoldPattern[a[2]] :> somethingImmediate}
somethingDelayed
somethingImmediate
Run Code Online (Sandbox Code Playgroud)

现在,如果我们要评估:

a = somethingThatScrewsUpHeads;
(* OwnValues[a] above stored in OwnValues *)
a[1]
a[2]
Run Code Online (Sandbox Code Playgroud)

我们得到......

somethingThatScrewsUpHeads[1]
somethingThatScrewsUpHeads[2]
Run Code Online (Sandbox Code Playgroud)

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

WRe*_*ach 12

我不知道这是否是一种"接受"的方式,但你可以定义一个阻止SetSetDelayed采取行动的规则a:

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.";
Run Code Online (Sandbox Code Playgroud)

有了这个规则,任何分配OwnValueto的尝试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
Run Code Online (Sandbox Code Playgroud)

但是,此规则仍将允许新DownValuesa:

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

In[20]:= a[3]

Out[20]= now

In[21]:= a[4]

Out[21]= later
Run Code Online (Sandbox Code Playgroud)

性能

该规则似乎没有对性能产生明显影响,Set并且SetDelayed可能是因为该规则是作为一个高价值安装的a.我试图通过执行来验证这一点...

Timing@Do[x = i, {i, 100000000}]
Run Code Online (Sandbox Code Playgroud)

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

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

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


Leo*_*rin 9

我不知道有什么方法可以直接"块" OwnValues,并自Mathematica的评估别的(零件,应用前评估的头DownValues,UpValuesSubValues等),这不会给我们带来任何地方(我简短地讨论了这个问题, 在我的书).

用简单的方法的问题是,它可能会被基础上,加入DownValuesSetSetDelayed,因为它看起来像 他们无法通过UpValues超载.

编辑

正如在评论中指出通过@WReach,在手的情况下UpValues可以成功地使用,因为我们正在处理SymbolS的必须在字面上本Set/ SetDelayed,因此标签深度1是足够的.我的注释与重新定义Set某些头部更相关,并且必须允许将具有这些头部的表达式存储在变量中(例如Part分配或由头部区分的自定义数据类型)

结束编辑

然而,增加DownValuesSetSetDelayed是在多数情况下一个灾难(这个线程是很能说明问题),并应使用非常罕见(如果有的话),并格外小心.

从不那么极端的方法来看,也许最简单,最安全但不自动的方式是Protect在定义它们之后的符号.此方法存在一个问题,即如果不使用Unprotect符号,您将无法添加新的或修改现有定义.

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

ClearAll[def];
SetAttributes[def, HoldAll];
def[(op : (Set | SetDelayed))[lhs_, rhs_]] /; 
    Head[Unevaluated[lhs]] =!= Symbol || DownValues[lhs] === {} := 
         op[lhs, rhs]
Run Code Online (Sandbox Code Playgroud)

并且始终采用基于包装SetDelayedSet分配的分配def(我选择了这种语法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}
Run Code Online (Sandbox Code Playgroud)

然后,您可以进一步编写代码处理宏,它将在代码中的任何位置进行包装SetDelayedSet基于分配def:

SetAttributes[useDef, HoldAll];
useDef[code_] := ReleaseHold[Hold[code] /. {x : (_Set | _SetDelayed) :> def[x]}]
Run Code Online (Sandbox Code Playgroud)

因此,您可以将您的代码包装起来useDef,然后根本不必更改那段代码.例如:

In[31]:= 
useDef[
   Clear[a];
   a[1]:=somethingDelayed;
   a[2]=somethingImmediate;
   a=somethingThatScrewsUpHeads;
]

In[32]:= {a[1],a[2]}
Out[32]= {somethingDelayed,somethingImmediate}
Run Code Online (Sandbox Code Playgroud)

在交互式会话中,您可以更进一步设置$Pre = useDef,然后您将不会忘记将代码包装进去useDef.

编辑

def通过使用模式匹配器来添加诊断功能是微不足道的.这是一个版本,如果DownValues尝试分配符号,将发出警告消息:

ClearAll[def];
SetAttributes[def, HoldAll];
def::ownval = 
  "An assignment to a symbol `1` with existing DownValues has been attempted";
def[(op : (Set | SetDelayed))[lhs_, rhs_]] /; 
  Head[Unevaluated[lhs]] =!= Symbol || DownValues[lhs] === {} := op[lhs, rhs]
def[(Set | SetDelayed)[sym_, _]] := 
  Message[def::ownval, Style[HoldForm[sym], Red]];
Run Code Online (Sandbox Code Playgroud)

同样,通过使用useDef[](可能带有$Pre),这可以是一种有效的调试工具,因为根本不需要对原始代码进行任何更改.