Особенности использования и внутренней работы функций * Set *
Я заметил только одну недокументированную функцию внутренней работы функций *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 больше не работает перегрузки. Внутреннее ядро был создан механизм, который другие вещи, позволяет J/Link избегать необходимость специальных, эр, "трюков", с помощью Set.
J/Link перегружен Набор из очень начало, почти двенадцать лет тому назад. Это позволяет поддерживать это синтаксис для назначения значения Java поле:
[email protected] = value
Перегруженное определение Set приводит к замедлению назначения форма
_Symbol[_Symbol] = value
Конечно, назначение - быстрое так что замедление в реальные условия. Только узкоспециализированные типы программ, вероятно, будут значительно пострадали.
Перегрузка Set не вызывает вызов Java на заданиях, которые делают не связаны с объектами Java (это быть очень дорогостоящим). Это можно проверить с простым использованием TracePrint on ваш a [b] = c.
Он, как вы заметили, делает небольшое изменение поведения присвоений что соответствует _Symbol [_Symbol] = значение. В частности, в f [_Symbol] = значение f получает оценку дважды. Это может вызвать проблемы для кода со следующими (очень необычная) форма:
f := SomeProgramWithSideEffects[]
f[x] = 42
Я не могу вспомнить, когда-либо видел "настоящие" код, подобный этому, или видя проблему сообщенный пользователем.
Теперь все это в 8.0.
Ответы
Ответ 1
Принимая во внимание случай UpSet
, это ожидаемое поведение. Можно написать:
5[b] ^= 1
Назначение выполняется для b
не Integer 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 нет символов.
Ответ 2
Поведение, которое вы показываете, похоже, является ошибкой в 7.0.1 (и, возможно, ранее), которая была исправлена в Mathematica 8. В Mathematica 8 оба из ваших исходных примеров a[b] = 2
и a[1] = 2
дают ошибку Set::write ... is protected
.
Проблема, по-видимому, связана с приведенной JLink снижающейся стоимостью Set
, которую вы идентифицировали. Это правило реализует синтаксис JLink, используемый для назначения значения полю объекта Java, например. [email protected] = value
.
Set
в Mathematica 8 не имеет этого определения. Мы можем принудительно повторно добавить подобное определение, таким образом:
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] = ...
. Хорошее избавление?