Вот концептуально простое решение - вы добавляете новое правило «Вверх» для обработки назначения:
Unprotect[Subscript];
Subscript[x_, i_, j_] := x[[2^i + j]]
Set[Subscript[x_, i_, j_], v_] ^:= x[[2^i + j]] = v;
Protect[Subscript];
(*Binomial Tree*)
y = {.1, {.2, .3}} // Flatten
Subscript[y, 1, 1]
Subscript[y, 1, 1] = .5;
Subscript[y, 1, 1]
Вам необходимо отдельное правило для обработки назначений (Set
, =
)в противном случае вы пытаетесь присвоить самому выражению Subscript при выполнении Subscript[y, 1, 1] = .5
Хотя вышеприведенное решение может * буквально использоваться , оно, вероятно, не должно использоваться, поскольку оно переопределяет Subscript
для всех типов первого аргумента.Такие переопределения могут быть небезопасными - они могут вступать в противоречие с другими, возможно желательными видами использования Subscript
Например, вызов Subscript для некоторого произвольного символа x приводит к сообщению об ошибке и оценке, которая может нам не понадобиться:
In[137]:= Subscript[x, 1, 2]
During evaluation of In[137]:= Part::partd: Part specification x[[4]] is
longer than depth of object. >>
Out[137]= x[[4]]
Более безопасной альтернативой будет назначение некоторой специальной заголовка (например, тега) длядвоичные деревья, для которых вы хотите переопределить Subscript
и использовать шаблоны, чтобы соответствующим образом ограничить область действия этих переопределений.Вот как это может выглядеть:
Unprotect[btree, Subscript];
ClearAll[btree, Subscript];
Subscript[x_btree, i_, j_] := x[[1, 2^i + j]]
Set[Subscript[x_, i_, j_], v_] ^:= (x[[1, 2^i + j]] = v) /; Head[x] === btree;
Protect[btree, Subscript];
Вы присваиваете своей структуре btree переменную следующим образом:
In[156]:= y = btree[{.1, .2, .3}]
Out[156]= btree[{0.1, 0.2, 0.3}]
Затем
In[157]:= Clear[x];
Subscript[y, 1, 1]
Subscript[y, 1, 1] = .5;
Subscript[y, 1, 1]
Subscript[x, 1, 1]
Out[158]= 0.3
Out[160]= 0.5
Out[161]= Subscript[x, 1, 1]
Таким образоммы уменьшаем возможные нежелательные эффекты, которые такие переопределения могут оказывать на какой-то другой код (остальная часть системы).
Оглядываясь назад на определение, включающее Set
, следует отметить, что мы не могли использоватьпростой шаблон, такой как Set[Subscript[x_btree, i_Integer, j_Integer],v_]:=...
, так как переменная (y
здесь) еще не будет иметь значение внутри Set
, когда шаблон соответствует, поэтому он не будет совпадать.Использование Condition
(/;
) - это всего лишь один из способов вывести переменную, которую мы назначаем, из Set
и заставить ее вычислять.Итак, если это y
, то Head[y]
приведет к оценке y
- это тот случай, когда мы действительно хотим заголовок вычисляемого выражения.В паттерне, подобном x_btree
, мы не даем x
возможности оценить до того, как будет предпринята попытка сопоставления с паттерном, и, следовательно, паттерн не совпадает (так как он все еще остается символом y
).
Используемое здесь дополнительное правило называется UpValue
.Для создания таких правил используется специальный синтаксис (оператор ^:=
- UpSetDelayed
, это один из способов создания UpValues
).UpValues
- важный механизм для «мягкой» перегрузки функций (включая системные функции), а также создания пользовательских типов данных.Чтобы прочитать о них, хорошей отправной точкой является здесь .