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
我不知道这是否是一种"接受"的方式,但你可以定义一个阻止Set
和SetDelayed
采取行动的规则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)
有了这个规则,任何分配OwnValue
to的尝试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)
但是,此规则仍将允许新DownValues
的a
:
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)
同样,即使有如此多的高价值规则,时机也没有改变.这些结果表明,从性能角度来看,这种技术是可以接受的,尽管我强烈建议您在特定应用的环境中执行性能测试.
我不知道有什么方法可以直接"块" OwnValues
,并自Mathematica的评估别的(零件,应用前评估的头DownValues
,UpValues
和SubValues
等),这不会给我们带来任何地方(我简短地讨论了这个问题, 在我的书).
用简单的方法的问题是,它可能会被基础上,加入DownValues
到Set
和SetDelayed
,因为它看起来像 他们无法通过UpValues超载.
编辑
正如在评论中指出通过@WReach,在手的情况下UpValues
可以成功地使用,因为我们正在处理Symbol
S的必须在字面上本Set
/ SetDelayed
,因此标签深度1
是足够的.我的注释与重新定义Set
某些头部更相关,并且必须允许将具有这些头部的表达式存储在变量中(例如Part
分配或由头部区分的自定义数据类型)
结束编辑
然而,增加DownValues
的Set
和SetDelayed
是在多数情况下一个灾难(这个线程是很能说明问题),并应使用非常罕见(如果有的话),并格外小心.
从不那么极端的方法来看,也许最简单,最安全但不自动的方式是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)
并且始终采用基于包装SetDelayed
和Set
分配的分配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)
然后,您可以进一步编写代码处理宏,它将在代码中的任何位置进行包装SetDelayed
和Set
基于分配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
),这可以是一种有效的调试工具,因为根本不需要对原始代码进行任何更改.