Redefine Subscript in Mathematica
I'd like to redefine Subscript
such that the "binomial tree coordinates" are translated into "flat array coordinates":
Unprotect[Subscript];
Subscript[x_, i_, j_] := x[[2 ^ i + j]];
Protect[Subscript];
(* Binomial Tree *)
y = {.1, {.2, .3}} // Flatten;
Subscript[y, 1, 1]
Subscript[y, 1, 1开发者_运维知识库] = .5;
Subscript[y, 1, 1]
What I expect to get is .3, .5
. Instead I get Set::write : Tag Subscript in {.1, .2, .3}_1,1 is Protected
and the value is not assigned. Please advise.
Here is the conceptually simplest solution - you add a new "Up" - rule to handle the assignment:
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]
You need a separate rule for handling assignments (Set
, =
), otherwise you are attempting to assign to the Subscript expression itself when you do Subscript[y, 1, 1] = .5
While the above solution can be used literally, it probably should not, since it redefines Subscript
for all types of the first argument. Such redefinitions may be unsafe - they may clash with other possibly desirable uses of Subscript
. For example, calling Subscript on some arbitrary symbol x results in an error-message, and evaluation which we may not want:
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]]
A safer alternative would be to assign some special head (like a tag) for the binary trees on which you want to redefine Subscript
, and use patterns to restrict the scope of these redefinitions accordingly. Here is how it might look:
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];
You assign you btree structure to the variable like this:
In[156]:= y = btree[{.1, .2, .3}]
Out[156]= btree[{0.1, 0.2, 0.3}]
Then,
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]
In this way, we reduce the possible unwanted effects that such redefinitions can have on some other code (rest of the system).
Looking back at the definition involving Set
, one thing to note is that we could not have used a simple pattern like Set[Subscript[x_btree, i_Integer, j_Integer],v_]:=...
, since the variable (y
here) would not yet evaluate to a value inside Set
when the pattern is matched, so it won't match. Using Condition
(/;
) is just one way to bring the variable we are assigning to, out of Set
, and make it evaluate. So, if it is y
, then Head[y]
will cause y
to evaluate - this is the case where we actually want the head of the evaluated expression. In pattern like x_btree
, we don't give x
a chance to evaluate before the pattern-matching attempt takes place, and therefore the pattern does not match (since it is still a symbol y
there).
The additional rule used here is called an UpValue
. To create such rules, a special syntax is used (^:=
operator - UpSetDelayed
, is one way to create UpValues
). UpValues
are an important mechanism for "soft" overloading of functions (including system functions), and also creation of custom data types. To read about them, a good starting point is here.
精彩评论