Ale*_*kov 5 wolfram-mathematica
我刚刚注意到Mathematica中*Set*函数内部工作的一个未记录的特性.
考虑:
In[1]:= a := (Print["!"]; a =.; 5);
a[b] = 2;
DownValues[a]
During evaluation of In[1]:= !
Out[3]= {HoldPattern[a[b]] :> 2}
Run Code Online (Sandbox Code Playgroud)
但
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}
Run Code Online (Sandbox Code Playgroud)
造成这种差异的原因是什么?为什么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
Run Code Online (Sandbox Code Playgroud)
如您所见,我们获得了5[b]避免 Protected标记属性的工作定义,Integer这通常会导致错误:
In[13]:= 5[b] = 1
During evaluation of In[13]:= Set::write: Tag Integer in 5[b] is Protected. >>
Out[13]= 1
Run Code Online (Sandbox Code Playgroud)
避免此错误的另一种方法是使用TagSet*:
In[15]:= b /: 5[b] = 1
UpValues[b]
Out[15]= 1
Out[16]= {HoldPattern[5[b]] :> 1}
Run Code Online (Sandbox Code Playgroud)
为什么这些功能?
关于我的问题,为什么我们可以写,a := (a =.; 5); a[b] = 2而不能a := (a =.; 5); a[1] = 2.在Mathematica 5中我们也写不出来a := (a =.; 5); a[b] = 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
Run Code Online (Sandbox Code Playgroud)
(以上内容复制自Mathematica 5.2)
当我们评估时,我们可以看到Mathematica的新版本内部会发生什么a := (a =.; 5); a[b] = 2:
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}
Run Code Online (Sandbox Code Playgroud)
我非常惊讶地看到在与语言相关的纯操作中调用Java,如为变量赋值.完全使用Java进行此类操作是否合理?
Todd Gayley(Wolfram Research)解释了这种行为:
首先,我要指出,在Mathematica 8中,J/Link不再重载Set.创建了一个内部内核机制,除其他外,允许J/Link避免使用Set的特殊,呃"技巧".
从大约十二年前开始,J/Link已经从一开始就超载了Set.这允许它支持这种语法来为Java字段赋值:
Run Code Online (Sandbox Code Playgroud)javaObject@field = valueSet的重载定义会导致表单分配减慢
Run Code Online (Sandbox Code Playgroud)_Symbol[_Symbol] = value当然,分配是一种快速操作,因此实际上减速很小.只有高度专业化的程序可能会受到严重影响.
该设置过载并不会导致对不涉及Java对象分配到Java的调用(这将是非常昂贵).这可以通过在a [b] = c上简单使用TracePrint来验证.
正如您所注意到的,它会对与_Symbol [_Symbol] = value匹配的赋值行为稍作更改.具体来说,在f [_Symbol] = value中,f被评估两次.对于具有以下(非常不寻常)形式的代码,这可能会导致问题:
Run Code Online (Sandbox Code Playgroud)f := SomeProgramWithSideEffects[] f[x] = 42我不记得曾经看过这样的"真实"代码,或者看到用户报告的问题.
这在8.0现在都没有了.
以第一种情况为例UpSet,这是预期的行为。可以这样写:
5[b] ^= 1
Run Code Online (Sandbox Code Playgroud)
分配的b不是 Integer 5。
关于Setand SetDelayed,虽然它们具有 Hold 属性,但它们仍然在内部评估表达式。这允许诸如以下的事情:
p = n : (_List | _Integer | All);
f[p] := g[n]
Run Code Online (Sandbox Code Playgroud)
测试:
f[25]
f[{0.1, 0.2, 0.3}]
f[All]
Run Code Online (Sandbox Code Playgroud)
克[25]
g[{0.1,0.2,0.3}]
瘿]
可以看到头部区域也被评估。这至少对以下有用UpSet:
p2 = head : (ff | gg);
p2[x] ^:= Print["Echo ", head];
ff[x]
gg[x]
Run Code Online (Sandbox Code Playgroud)
回声关闭
回声gg
很容易看出它也会发生在Set,但我不太清楚这有什么用:
j = k;
j[5] = 3;
DownValues[k]
(* Out= {HoldPattern[k[5]] :> 3} *)
Run Code Online (Sandbox Code Playgroud)
我对你问题第一部分的分析是错误的。我现在不明白为什么a[b] = 2被接受而不a[1] = 2被接受。也许在分配的某个阶段,第二个出现为5[1] = 2并且模式检查引发错误,因为 LHS 上没有符号。