*Set*函数的使用和内部工作细节 [英] Specifics of usage and internal work of *Set* functions
问题描述
我刚刚注意到 Mathematica 中 *Set*
函数内部工作的一个未记录的特性.
考虑:
In[1]:= a := (Print["!"]; a =.; 5);[b] = 2;向下值[a]在评估 In[1]:= 期间!Out[3]= {HoldPattern[a[b]] :>2}
但是
In[4]:= a := (Print["!"]; a =.; 5);[1] = 2;向下值[a]在评估 In[4]:= 期间!在评估 In[4]:= Set::write: 5[1] 中的标记整数是受保护的.>>Out[6]= {HoldPattern[a[b]] :>2}
造成这种差异的原因是什么?尽管 Set
具有属性 HoldFirst
,但为什么要计算 a
?这种行为对哪些目的有用?
还要注意这种情况:
In[7]:= a := (Print["!"]; a =.; 5)[b] ^= 2上值[b][b]在评估 In[7]:= 期间!出[8]= 2Out[9]= {HoldPattern[5[b]] :>2}出[10]= 2
如您所见,我们得到了 5[b]
的工作定义,避免了通常会导致错误的标签 Integer
的 Protected
属性案例:
输入[13]:= 5[b] = 1在评估 In[13]:= Set::write: 5[b] 中的标记整数是受保护的.>>出[13]= 1
避免此错误的另一种方法是使用TagSet*
:
In[15]:= b/: 5[b] = 1上值[b]出[15]= 1Out[16]= {HoldPattern[5[b]] :>1}
为什么会有这些功能?
<小时>关于我的问题,为什么我们可以写 a := (a =.; 5);a[b] = 2
而不能 a := (a =.; 5);a[1] = 2
.实际上在 Mathematica 5 中我们不能写 a := (a =.; 5);a[b] = 2
也是:
输入[1]:=a:=(a=.;5);a[b]=2From In[1]:= Set::write: Tag Integer in 5[b] is Protected.更多的...输出[1]=2
(以上复制自Mathematica 5.2)
当我们评估 a := (a =.; 5); 时,我们可以看到在新版本的 Mathematica 内部发生了什么.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},规则条件on[$ConditionHold[$ConditionHold[JLink`CallJava`Private`setField[5[b],2]]],False],Fail},a[b]=2,{a[b],{a},{b},a[b]},2}
我很惊讶地看到在像为变量赋值这样的纯语言相关操作中调用 Java.使用 Java 进行此类操作是否合理?
<小时>Todd Gayley(Wolfram 研究)已解释 这种行为:
<块引用>首先,让我指出Mathematica 8, J/Link 不再重载设置.内部内核建立了机制,其中其他事情,允许 J/Link 避免需要特殊的,呃,技巧"与设置.
J/Link 已经从一开始,将近十二年前.这允许它支持这个为 Java 赋值的语法字段:
javaObject@field = value
Set 的重载定义导致分配放缓表格
_Symbol[_Symbol] = 值
当然,赋值是一个快操作,所以减速很小实际条款.只有高度专业化程序类型可能是受到明显影响.
Set 重载不会导致在执行任务时调用 Java不涉及 Java 对象(这将非常昂贵).这个可以验证简单地使用 TracePrint你的 a[b]=c.
正如你所指出的,它确实会产生轻微的影响任务行为的改变匹配 _Symbol[_Symbol] = 值.具体来说,在 f[_Symbol] = value 中,f被评估两次.这会导致以下代码的问题(极不寻常的)形式:
f := SomeProgramWithSideEffects[]f[x] = 42
我不记得曾经看过真实"像这样的代码,或者看到一个问题由用户报告.
这一切在 8.0 中都没有实际意义.
首先以 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}]落下]
g[25]
g[{0.1, 0.2, 0.3}]
g[全部]
可以看到头部区域也进行了评估.这至少对 UpSet
很有用:
p2 = head : (ff | gg);p2[x] ^:= Print["Echo ", head];ff[x]gg[x]
<块引用>
回声关闭
回声gg
很容易看出它也发生在 Set
中,但我不太清楚这会有什么用处:
j = k;j[5] = 3;向下值[k](* Out= {HoldPattern[k[5]] :> 3} *)
<小时>
我对你问题第一部分的分析是错误的.我现在不明白为什么 a[b] = 2
被接受而 a[1] = 2
不被接受.也许在分配的某个阶段,第二个出现为 5[1] = 2
并且模式检查引发错误,因为 LHS 上没有符号.
I just noticed one undocumented feature of internal work of *Set*
functions in Mathematica.
Consider:
In[1]:= a := (Print["!"]; a =.; 5);
a[b] = 2;
DownValues[a]
During evaluation of In[1]:= !
Out[3]= {HoldPattern[a[b]] :> 2}
but
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}
What is the reason for this difference? Why a
is evaluated although Set
has attribute HoldFirst
? For which purposes such behavior is useful?
And note also this case:
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
As you see, we get the working definition for 5[b]
avoiding Protected
attribute of the tag Integer
which causes error in usual cases:
In[13]:= 5[b] = 1
During evaluation of In[13]:= Set::write: Tag Integer in 5[b] is Protected. >>
Out[13]= 1
The other way to avoid this error is to use TagSet*
:
In[15]:= b /: 5[b] = 1
UpValues[b]
Out[15]= 1
Out[16]= {HoldPattern[5[b]] :> 1}
Why are these features?
Regarding my question why we can write a := (a =.; 5); a[b] = 2
while cannot a := (a =.; 5); a[1] = 2
. In really in Mathematica 5 we cannot write a := (a =.; 5); a[b] = 2
too:
In[1]:=
a:=(a=.;5);a[b]=2
From In[1]:= Set::write: Tag Integer in 5[b] is Protected. More...
Out[1]=
2
(The above is copied from Mathematica 5.2)
We can see what happens internally in new versions of Mathematica when we evaluate 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}
I was very surprised to see calls to Java in such a pure language-related operation as assigning a value to a variable. Is it reasonable to use Java for such operations at all?
Todd Gayley (Wolfram Research) has explained this behavior:
At the start, let me point out that in Mathematica 8, J/Link no longer overloads Set. An internal kernel mechanism was created that, among other things, allows J/Link to avoid the need for special, er, "tricks" with Set.
J/Link has overloaded Set from the very beginning, almost twelve years ago. This allows it support this syntax for assigning a value to a Java field:
javaObject@field = value
The overloaded definition of Set causes a slowdown in assignments of the form
_Symbol[_Symbol] = value
Of course, assignment is a fast operation, so the slowdown is small in real terms. Only highly specialized types of programs are likely to be significantly affected.
The Set overload does not cause a call to Java on assignments that do not involve Java objects (this would be very costly). This can be verified with a simple use of TracePrint on your a[b]=c.
It does, as you note, make a slight change in the behavior of assignments that match _Symbol[_Symbol] = value. Specifically, in f[_Symbol] = value, f gets evaluated twice. This can cause problems for code with the following (highly unusual) form:
f := SomeProgramWithSideEffects[] f[x] = 42
I cannot recall ever seeing "real" code like this, or seeing a problem reported by a user.
This is all moot now in 8.0.
Taking the case of UpSet
first, this is expected behavior. One can write:
5[b] ^= 1
The assignment is made to b
not the Integer 5
.
Regarding Set
and SetDelayed
, while these have Hold attributes, they still internally evaluate expressions. This allows things such as:
p = n : (_List | _Integer | All);
f[p] := g[n]
Test:
f[25]
f[{0.1, 0.2, 0.3}]
f[All]
g[25]
g[{0.1, 0.2, 0.3}]
g[All]
One can see that heads area also evaluated. This is useful at least for UpSet
:
p2 = head : (ff | gg);
p2[x] ^:= Print["Echo ", head];
ff[x]
gg[x]
Echo ff
Echo gg
It is easy to see that it happens also with Set
, but less clear to me how this would be useful:
j = k;
j[5] = 3;
DownValues[k]
(* Out= {HoldPattern[k[5]] :> 3} *)
My analysis of the first part of your question was wrong. I cannot at the moment see why a[b] = 2
is accepted and a[1] = 2
is not. Perhaps at some stage of assignment the second one appears as 5[1] = 2
and a pattern check sets off an error because there are no Symbols on the LHS.
这篇关于*Set*函数的使用和内部工作细节的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!