重新定义Mathematica中的下标

use*_*407 4 wolfram-mathematica pattern-matching variable-assignment

我想重新定义Subscript"二叉树坐标"被翻译成"平面阵列坐标":

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]
Run Code Online (Sandbox Code Playgroud)

我期望得到的是.3, .5.相反,我得到了,Set::write : Tag Subscript in {.1, .2, .3}_1,1 is Protected并没有分配价值.请指教.

Mr.*_*ard 6

这是概念上最简单的解决方案 - 您添加一个新的"Up" - 规则来处理分配:

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]
Run Code Online (Sandbox Code Playgroud)

您需要一个单独的规则来处理赋值(Set,=),否则您在尝试时会尝试分配给Subscript表达式Subscript[y, 1, 1] = .5

虽然上面的解决方案可以按字面意思使用,但它可能不应该使用,因为它重新定义Subscript了所有类型的第一个参数.这种重新定义可能是不安全的 - 它们可能与其他可能需要的用途发生冲突Subscript.例如,在某个任意符号x上调用Subscript会导致错误消息,以及我们可能不需要的评估:

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]]
Run Code Online (Sandbox Code Playgroud)

更安全的替代方法是为要重新定义的二叉树分配一些特殊头(如标记)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];
Run Code Online (Sandbox Code Playgroud)

您将btree结构分配给变量,如下所示:

In[156]:= y = btree[{.1, .2, .3}]

Out[156]= btree[{0.1, 0.2, 0.3}]
Run Code Online (Sandbox Code Playgroud)

然后,

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]
Run Code Online (Sandbox Code Playgroud)

通过这种方式,我们减少了这种重新定义可能对其他一些代码(系统的其余部分)产生的不良影响.

回顾一下涉及的定义Set,需要注意的一点是我们不能使用像这样的简单模式Set[Subscript[x_btree, i_Integer, j_Integer],v_]:=...,因为变量(y这里)Set在模式匹配时还没有评估到内部的值,所以它不匹配.使用Condition(/;)只是一种方法,可以将我们分配的变量,分配出来Set并进行评估.因此,如果是y,那么Head[y]将导致y评估 - 这是我们实际需要评估表达式的头部的情况.在模式中x_btree,我们没有x机会在模式匹配尝试发生之前进行评估,因此模式不匹配(因为它仍然是y那里的符号).

此处使用的附加规则称为UpValue.要创建此类规则,需要使用特殊语法(^:=operator - UpSetDelayed,是一种创建方法UpValues).UpValues是"软"重载函数(包括系统函数)的重要机制,也是自定义数据类型的创建.要了解它们,这里有一个很好的起点 .