Особенности использования и внутренней работы функций * Set * - PullRequest
5 голосов
/ 01 мая 2011

Я только что заметил одну недокументированную особенность внутренней работы *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 *

Ответы [ 2 ]

3 голосов
/ 01 мая 2011

Поведение, которое вы показываете, похоже, является ошибкой в ​​7.0.1 (и, возможно, ранее), которая была исправлена ​​в Mathematica 8. В Mathematica 8 оба ваших исходных примера a[b] = 2 и a[1] = 2 дают ошибку Set::write ... is protected.

Похоже, что проблема связана с указанным вами JLink-значением внизу Set.Это правило реализует синтаксис JLink, используемый для присвоения значения полю Java-объекта, например, object@field = 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] = ....Хорошее избавление?

3 голосов
/ 01 мая 2011

Сначала рассмотрим случай 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]
<b>   g[25]</b>
<b>   g[{0.1, 0.2, 0.3}]</b>
<b>   g[All]</b>

Видно, что область головы также оценивается.Это полезно по крайней мере для 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 нет символов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...