Мне неизвестен какой-либо способ напрямую "заблокировать" OwnValues
, и поскольку оценщик Mathematica оценивает головы раньше всего (части, применение DownValues
, UpValues
и SubValues
и т. Д.), Это делаетникуда нас не приведет (я кратко обсудил эту проблему в своей книге ).
Проблема с простым подходом состоит в том, что он, вероятно, будет основан на добавлении DownValues
к Set
иSetDelayed
, так как похоже на , они не могут быть перегружены через UpValues .
РЕДАКТИРОВАТЬ
Как указано @WReach в комментариях, для рассматриваемого случая можно успешно использовать UpValues
, поскольку мы имеем дело с Symbol
sкоторый должен буквально присутствовать в Set
/ SetDelayed
, и поэтому глубина тега 1
достаточна.Мой комментарий более уместен для переопределения Set
для некоторых заголовков, и когда необходимо разрешить сохранение выражений с этими заголовками в переменной (случаи, такие как Part
присваивания или пользовательские типы данных, различаемые по заголовкам)
END EDIT
Однако добавление DownValues
для Set
и SetDelayed
в большинстве случаев является причиной катастрофы ( этот поток очень показателен),и должен использоваться очень редко (если вообще) и с особой осторожностью.
Из менее экстремальных подходов, возможно, самый простой и безопасный, но не автоматический способ - Protect
символы после того, как вы их определили.У этого метода есть проблема в том, что вы не сможете добавлять новые или изменять существующие определения без Unprotect
-ing символа.
В качестве альтернативы, а также для автоматизации вещей, вы можете использовать ряд приемов.Один из них - определить пользовательские операторы присваивания, такие как
ClearAll[def];
SetAttributes[def, HoldAll];
def[(op : (Set | SetDelayed))[lhs_, rhs_]] /;
Head[Unevaluated[lhs]] =!= Symbol || DownValues[lhs] === {} :=
op[lhs, rhs]
и последовательно обернуть назначения SetDelayed
- и Set
в def
(я выбрал этот синтаксис для def
- сохранено Set
/ SetDelayed
внутри def
- для сохранения подсветки синтаксиса), и то же самое для Set
.Вот как будет выглядеть ваш пример:
In[26]:=
Clear[a];
def[a[1]:=somethingDelayed];
def[a[2]=somethingImmediate];
def[a=somethingThatScrewsUpHeads];
In[30]:= {a[1],a[2]}
Out[30]= {somethingDelayed,somethingImmediate}
Затем вы можете пойти дальше и написать макрос для обработки кода, который будет повсеместно переносить назначения на основе SetDelayed
и Set
в def
в вашем коде:
SetAttributes[useDef, HoldAll];
useDef[code_] := ReleaseHold[Hold[code] /. {x : (_Set | _SetDelayed) :> def[x]}]
Таким образом, вы можете просто обернуть свой кусок кода в useDef
, и тогда вам вообще не придется его менять.Например:
In[31]:=
useDef[
Clear[a];
a[1]:=somethingDelayed;
a[2]=somethingImmediate;
a=somethingThatScrewsUpHeads;
]
In[32]:= {a[1],a[2]}
Out[32]= {somethingDelayed,somethingImmediate}
В интерактивном сеансе вы можете пойти еще дальше и установить $Pre = useDef
, тогда вы не забудете свернуть свой код в useDef
.
РЕДАКТИРОВАТЬ
Добавить диагностические возможности к def
несложно, используя сопоставление с шаблоном.Вот версия, которая будет выдавать предупреждающее сообщение в случае попытки присвоения символа с DownValues
:
ClearAll[def];
SetAttributes[def, HoldAll];
def::ownval =
"An assignment to a symbol `1` with existing DownValues has been attempted";
def[(op : (Set | SetDelayed))[lhs_, rhs_]] /;
Head[Unevaluated[lhs]] =!= Symbol || DownValues[lhs] === {} := op[lhs, rhs]
def[(Set | SetDelayed)[sym_, _]] :=
Message[def::ownval, Style[HoldForm[sym], Red]];
Опять же, используя useDef[]
(возможно, с $Pre
), этоможет быть эффективным средством отладки, поскольку в исходный код вообще не требуется никаких изменений.