Я только что заметил одну недокументированную особенность внутренней работы *Set*
функций в Mathematica .
Рассмотрим:
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
тега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
, а не можем 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
(выше скопировано с 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}
Я был очень удивлен, увидев вызовы Java в такой чисто связанной с языком операции, какприсвоение значения переменной.Разумно ли вообще использовать Java для таких операций?
Тодд Гейли (Wolfram Research) объяснил такое поведение:
В началеПозвольте мне отметить, что в Mathematica 8 J / Link больше не перегружает Set.Был создан внутренний механизм ядра, который, помимо прочего, позволяет J / Link избегать необходимости в особых, т. Е. «Хитростях» с Set.
J / Link перегружает Set с самого начала, почти двенадцатьмного лет назад.Это позволяет ему поддерживать этот синтаксис для присвоения значения полю Java:
javaObject@field = value
Перегруженное определение Set вызывает замедление в назначениях вида
_Symbol[_Symbol] = value
Конечно, назначениеэто быстрая операция, поэтому замедление в реальном выражении мало.Только узкоспециализированные типы программ могут быть значительно затронуты.
Перегрузка Set не вызывает вызов Java для назначений, которые не включают объекты Java (это будет очень дорого),Это можно проверить с помощью простого использования TracePrint для вашего a [b] = c.
. Как вы заметили, оно немного меняет поведение назначений, соответствующих _Symbol [_Symbol] = value.В частности, при значении f [_Symbol] = значение f оценивается дважды.Это может вызвать проблемы для кода со следующей (весьма необычной) формой:
f := SomeProgramWithSideEffects[]
f[x] = 42
Я не могу вспомнить, чтобы когда-либо видел «настоящий» код, подобный этому, или видел проблему, о которой сообщил пользователь.
Это все спорно сейчас в 8.0.
1079 *