实际上,g /: f[g[x_]] := h[x]
不等于f[g[x_]] := h[x]
。后者将定义与f
, while (/:
) and (^=
和它的, ^:=
) 将定义与g
。这是一个至关重要的区别,可以通过一个简单的例子来说明。假设您想要一组服从模 5 加法的变量,即 6 + 7 mod 5 = 3。所以,我们想要任何Head
mod
行为正确。最初,我们会认为
a_mod + b_mod := mod@Mod[a + b, 5]
会工作。但是,它会产生错误
SetDelayed::write : Tag Plus in a_mod + b_mod is Protected.
我们可以删除Unprotect
Plus
然后我们的定义就可以工作,但这可能会导致其他定义出现问题,并且Plus
积累更多的定义,它会变慢。或者,我们可以将加法属性与mod
对象本身通过TagSet
mod /: mod[a_] + mod[b_] := mod @ Mod[a + b, 5]
or UpSetDelayed
mod[a_] + mod[b_] ^:= mod @ Mod[a + b, 5]
设置一个从概念的角度来看更正确,因为mod
是具有不同属性的那个。
有几个问题需要注意。首先,upvalue机制只能扫描一层深度,即Plus[a_mod, b_mod]
很好,但是Exp[Plus[a_mod, b_mod]]
会抛出错误。这可能需要您对中间类型发挥创意。其次,从编码角度UpSetDelayed
更容易写,但有时会出现一些歧义Head
是与之相关的上值。TagSet
通过显式命名适当的来处理这个问题Head
,一般来说,这是我更喜欢的UpSet
.
Mathematica 的一些运算符没有任何与其相关的行为,因此它们不受保护。对于这些运算符,您可以根据需要定义函数。例如,我定义了
a_ \[CircleTimes] b_ := KroneckerProduct[a,b]
a_ \[CircleTimes] b_ \[CircleTimes] c__ := a \[CircleTimes] ( b \[CircleTimes] c )
and
a_ \[CirclePlus] b__ := BlockDiagonal[{a,b}]
为我经常使用的矩阵运算提供方便的速记符号。
我上面的例子有点做作,但是很多时候UpValues
已经派上用场了。例如,我发现我需要一种符号形式来表示复数根,该符号形式在乘法和求幂下表现得适当。
Example:一个简单且有用的例子是标记Symbol
作为真实的:
makeReal[a__Symbol] := (
# /: Element[#, Reals] := True;
# /: Im[#] := 0;
# /: Re[#] := #;
# /: Abs[#] := Sign[#] #;
# /: Arg[#] := Piecewise[{{0, Sign[#] >= 0}, {Pi, Sign[#] < 0}}]
) & /@ List[a]
注意使用TagSet
as Element[ a, Reals ] ^:= True
会很含糊。该规则将附加到什么a
or Reals
?另外,如果我们想要一个正实数,我们可以设置Arg[#]:=0
这使得Simplify
按预期行事,例如Simplify[Sqrt[a^2]] == a
.