我刚刚注意到 Mathematica 中*Set*
函数内部工作的一个未记录的功能。
考虑:
In[1]:= a := (Print["!"]; a =.; 5);
a[b] = 2;
DownValues[a]
During evaluation of In[1]:= !
Out[3]= {HoldPattern[a[b]] :> 2}
但
In[4]:= a := (Print["!"]; a =.; 5);
a[1] = 2;
DownValues[a]
During evaluation of In[4]:= !
During evaluation of In[4]:= Set::write: Tag Integer in 5[1] is Protected. >>
Out[6]= {HoldPattern[a[b]] :> 2}
这种差异的原因是什么?尽管a
具有属性Set
,但为什么会对HoldFirst
进行评估?为什么这种行为很有用?
还要注意这个案例:
In[7]:= a := (Print["!"]; a =.; 5)
a[b] ^= 2
UpValues[b]
a[b]
During evaluation of In[7]:= !
Out[8]= 2
Out[9]= {HoldPattern[5[b]] :> 2}
Out[10]= 2
如您所见,我们获得5[b]
的工作定义,以避免标记Protected
的{{1}}属性,这会在通常情况下导致错误:
Integer
避免此错误的另一种方法是使用In[13]:= 5[b] = 1
During evaluation of In[13]:= Set::write: Tag Integer in 5[b] is Protected. >>
Out[13]= 1
:
TagSet*
为什么这些功能?
关于我的问题,为什么我们可以写In[15]:= b /: 5[b] = 1
UpValues[b]
Out[15]= 1
Out[16]= {HoldPattern[5[b]] :> 1}
而不能a := (a =.; 5); a[b] = 2
。在 Mathematica 5中,我们也不能写a := (a =.; 5); a[1] = 2
:
a := (a =.; 5); a[b] = 2
(以上内容摘自 Mathematica 5.2)
当我们评估In[1]:=
a:=(a=.;5);a[b]=2
From In[1]:= Set::write: Tag Integer in 5[b] is Protected. More...
Out[1]=
2
时,我们可以看到新版 Mathematica 内部会发生什么:
a := (a =.; 5); a[b] = 2
我很惊讶地看到在与语言相关的纯操作中调用Java,如为变量赋值。完全使用Java进行此类操作是否合理?
Todd Gayley(Wolfram Research)has explained这种行为:
一开始,让我指出一下 Mathematica 8,J / Link不再 重载设置。内核 其中创造了一种机制 其他的东西,允许J / Link避免 需要特殊的,呃,“技巧” 与Set。
J / Link已经重载了Set 一开始,差不多十二年了 前。这允许它支持这一点 用于为Java分配值的语法 字段:
In[1]:= a:=(a=.;5); Trace[a[b]=2,TraceOriginal->True] Out[2]= {a[b]=2,{Set},{2},a[b]=2,{With[{JLink`Private`obj$=a},RuleCondition[$ConditionHold[$ConditionHold[JLink`CallJava`Private`setField[JLink`Private`obj$[b],2]]],Head[JLink`Private`obj$]===Symbol&&StringMatchQ[Context[JLink`Private`obj$],JLink`Objects`*]]],{With},With[{JLink`Private`obj$=a},RuleCondition[$ConditionHold[$ConditionHold[JLink`CallJava`Private`setField[JLink`Private`obj$[b],2]]],Head[JLink`Private`obj$]===Symbol&&StringMatchQ[Context[JLink`Private`obj$],JLink`Objects`*]]],{a,a=.;5,{CompoundExpression},a=.;5,{a=.,{Unset},a=.,Null},{5},5},RuleCondition[$ConditionHold[$ConditionHold[JLink`CallJava`Private`setField[5[b],2]]],Head[5]===Symbol&&StringMatchQ[Context[5],JLink`Objects`*]],{RuleCondition},{Head[5]===Symbol&&StringMatchQ[Context[5],JLink`Objects`*],{And},Head[5]===Symbol&&StringMatchQ[Context[5],JLink`Objects`*],{Head[5]===Symbol,{SameQ},{Head[5],{Head},{5},Head[5],Integer},{Symbol},Integer===Symbol,False},False},RuleCondition[$ConditionHold[$ConditionHold[JLink`CallJava`Private`setField[5[b],2]]],False],Fail},a[b]=2,{a[b],{a},{b},a[b]},2}
Set的重载定义 导致任务减速放缓 表格
javaObject@field = value
当然,任务很快 操作,所以减速很小 实际条款。只有高度专业化 程序的类型可能是 受到严重影响。
Set重载不导致a 在有关的任务上调用Java 不涉及Java对象(这会 非常昂贵)。这可以验证 只需使用TracePrint即可 你的a [b] = c。
正如你所注意到的那样 改变作业的行为 匹配_Symbol [_Symbol] =值。 具体来说,在f [_Symbol] = value,f 得到两次评估。这可能会导致 具有以下代码的问题 (非常不寻常)形式:
_Symbol[_Symbol] = value
我不记得曾见过“真实” 像这样的代码,或者看到问题 用户报告。
现在8.0版本都没有了。
答案 0 :(得分:3)
首先考虑UpSet
的情况,这是预期的行为。人们可以写:
5[b] ^= 1
分配是b
而不是整数5
。
关于Set
和SetDelayed
,虽然它们具有Hold属性,但它们仍在内部评估表达式。这允许以下内容:
p = n : (_List | _Integer | All);
f[p] := g[n]
测试:
f[25]
f[{0.1, 0.2, 0.3}]
f[All]
g[25]
g[{0.1, 0.2, 0.3}]
g[All]
可以看到头部区域也进行了评估。这至少对UpSet
:
p2 = head : (ff | gg);
p2[x] ^:= Print["Echo ", head];
ff[x]
gg[x]
Echo ff
Echo gg
很容易看出它也发生在Set
上,但对我来说不太清楚这是如何有用的:
j = k;
j[5] = 3;
DownValues[k]
(* Out= {HoldPattern[k[5]] :> 3} *)
我对你问题第一部分的分析是错误的。我现在无法理解为什么a[b] = 2
被接受而a[1] = 2
不被接受。也许在分配的某个阶段,第二个显示为5[1] = 2
,并且模式检查会引发错误,因为LHS上没有符号。
答案 1 :(得分:3)
您显示的行为似乎是在Mathematica 8中修复的7.0.1(可能更早)中的错误。在Mathematica 8中,您的原始a[b] = 2
和a[1] = 2
示例都给出了Set::write ... is protected
错误。
问题似乎源于您确定的与{J}相关的Set
下限值。该规则实现了用于为Java对象的字段赋值的JLink语法,例如, object@field = value
。
Set
没有那个定义。我们可以强行重新添加类似的定义,因此:
Unprotect[Set]
HoldPattern[sym_Symbol[arg_Symbol]=val_] :=
With[{obj=sym}
, setField[obj[arg], val] /; Head[obj] === Symbol && StringMatchQ[Context[obj],"Something`*"]
]
在Mathematica 8中安装此定义后,它现在表现出与Mathematica 7中相同的不一致行为。
我认为现在通过其他方式完成JLink对象字段分配。有问题的规则看起来可能会对表单Head
的每个评估添加代价高昂的StringMatchQ
和a[b] = ...
测试。好摆脱?