这里提供概念上最简单的解决方案 - 添加一个新的"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];
您可以将B树结构分配给变量,方法如下:
您可以像这样将B树结构分配给变量:
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”)在模式匹配时还没有计算出值,所以它不匹配。使用“Condition”(“/;”)只是一种将我们要分配的变量从“Set”中提取并使其计算的方法。因此,如果是“y”,则“Head[y]”将导致“y”计算 - 这是我们实际上想要评估表达式的头的情况。在像“x_btree”这样的模式中,在模式匹配尝试发生之前,我们不给“x”评估的机会,因此模式不匹配(因为它仍然是一个符号“y”)。
这里使用的附加规则称为“UpValue”。要创建此类规则,需要使用特殊语法(“^:=”运算符 - “UpSetDelayed”是创建“UpValues”的一种方法)。“UpValues”是“软”重载函数(包括系统函数)和创建自定义数据类型的重要机制。要了解有关它们的信息,请参阅
此处。