我想重新定义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]
我期望得到的是.3, .5
。相反,我得到Set::write : Tag Subscript in {.1, .2, .3}_1,1 is Protected
并且未分配值。请指教。
答案 0 :(得分: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]
您需要一个单独的规则来处理分配(Set
,=
),否则当您执行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]]
更安全的替代方法是为要重新定义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
是“软”重载函数(包括系统函数)的重要机制,也是自定义数据类型的创建。要了解它们,一个很好的起点是here。