对我而言,g /: f[g[x_]] := h[x]
只是等同于f[g[x_]] := h[x]
。你能提出一个你必须使用/:
的例子吗?
答案 0 :(得分:28)
实际上,g /: f[g[x_]] := h[x]
并不等同于f[g[x_]] := h[x]
。后者将定义与f
相关联,而TagSet
(/:
)和UpSet
(^=
及其delayed version,^:=
)将定义与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 /: a_mod + b_mod := mod @ Mod[a + b, 5]
或UpSetDelayed
a_mod + b_mod ^:= mod @ Mod[a + b, 5]
从概念的角度来看,设置upvalue更为正确,因为mod
是具有不同属性的那个。
有几个问题需要注意。首先,upvalue机制只能扫描一个级别,即Plus[a_mod, b_mod]
很好,但Exp[Plus[a_mod, b_mod]]
会抛出错误。这可能需要您使用中间类型进行创作。其次,从编码角度来看,UpSetDelayed
更容易编写,但偶尔会有一些含糊不清的Head
是与之相关的upvalue。 TagSet
通过明确命名相应的Head
来处理这一问题,而且一般情况下,我倾向于优先于UpSet
。
Mathematica的一些操作员没有任何与之相关的行为,因此他们没有受到保护。对于这些运算符,您可以根据需要定义函数。例如,我已经定义了
a_ \[CircleTimes] b_ := KroneckerProduct[a,b]
a_ \[CircleTimes] b_ \[CircleTimes] c__ := a \[CircleTimes] ( b \[CircleTimes] c )
和
a_ \[CirclePlus] b__ := BlockDiagonal[{a,b}]
为我经常使用的矩阵运算提供方便的简写符号。
我上面的例子有点做作,但有很多次UpValues
派上用场了。例如,我发现我需要一种符号形式,用于在乘法和幂运算中表现得恰当的复杂统一根。
示例:一个简单有用的示例是将Symbol
标记为Real:
makeReal[a__Symbol] := (
# /: Element[#, Reals] := True;
# /: Im[#] := 0;
# /: Re[#] := #;
# /: Abs[#] := Sign[#] #;
# /: Arg[#] := Piecewise[{{0, Sign[#] >= 0}, {Pi, Sign[#] < 0}}]
) & /@ List[a]
请注意,使用TagSet
作为Element[ a, Reals ] ^:= True
将是不明确的。规则与a
或Reals
相关联的内容是什么?另外,如果我们想要一个正实数,我们可以设置Arg[#]:=0
,Simplify
允许Simplify[Sqrt[a^2]] == a
按预期行事,例如{{1}}。
答案 1 :(得分:16)
除了@rcollyer的优秀答案之外,我还想强调一些关于UpValues
的其他重要事项。
一个非常重要的方面是它们允许您仅在某些符号上“轻柔地”重载某些系统功能。 @rcollyer指出了这一点的重要性,但不能过分强调 - 这会使代码本地化,并大大降低代码可以全局交互并影响系统的其他部分或其他部分的机会。用户定义的代码,与Unprotect
系统符号不同,并向其添加一些DownValues
。
除了安全和本地之外,如果使用像yourSymbol/:f_[_yourSymbol,rest___]:=...
这样的结构,这种重新定义也可能非常普遍。这些应该谨慎使用,但有时可以提供非常简洁和简单的解决方案。 Here是一个示例,其中一个代码可以用于一次“重载”多个系统函数,为它们提供额外的非平凡功能。
下一点是评估。您可以遇到的常见声明是“UpValues
之前应用了DownValues
”。必须明确这一点:对于f[g[args]]
,这意味着UpValues
g
DownValues
f
UpValues
g
之前的DownValues
适用于g
,前提是评估过程已经全部完成“向下”到最里面的部分,然后回到“向上”。特别是,这并不意味着g[args]
的{{1}}将在f
之前应用g
- 如果DownValues
可以在Hold
内进行评估, UpValues
具有适当的g[args]
,它会(除非f具有f[result-of-evaluation-of g[args]]
- 属性之一),并且In[58]:=
ClearAll[f, g];
f[x_] := x^2;
g /: f[g[x_]] := Sin[g[x]];
g[x_] := Cos[x];
In[62]:= f[g[y]]
Out[62]= Cos[y]^2
的存在不会阻止这种情况,因为(对于标准评估),UpValues
的评估发生在评估g
之前。例如,这里:
g[y]
Cos[y]
的{{1}}无法申请,因为f
在之前的评估步骤中已转换为HoldAll
。非标准评估的情况会有所不同 - 要么我们提供HoldFirst
属性g[y]
或Unevaluated
,要么我们将g[y]
包裹在In[63]:= f[Unevaluated[g[y]]]
Out[63]= Sin[Cos[y]]
中 - 在这两种情况下,我们都会给评估者提供跳过UpValues
评估的指令:
Hold
这一点与上一点有关:应该知道,即使在具有UpValue
- 属性的头部内也会执行搜索DownValue
,因此基于In[64]:= ClearAll[f,ff];
f[x_]:=Print["Evaluated"];
ff/:h_[ff[x_]]:=Print["Evaluated"];
In[67]:= Hold[f[1]]
Out[67]= Hold[f[1]]
In[68]:= Hold[ff[1]]
During evaluation of In[68]:= Evaluated
的定义可以即使基于类似外观UpValues
的基础上也不会评估。例如:
HoldAllComplete
如果想要绝对阻止搜索In[69]:= {HoldComplete[f[1]],HoldComplete[ff[1]]}
Out[69]= {HoldComplete[f[1]],HoldComplete[ff[1]]}
,则应该为UpValues
属性提供一个函数。例如:
Set
@rcollyer已经提到了这一点。引入了该限制以用于模式匹配器/评估器的效率。我只想强调它的一个重要且非显而易见的结果:看起来你不能使用In[74]:=
ClearAll[a,myType,myCustomCode,newValue];
myType/:Set[var_myType,rhs_]:=myCustomCode;
来重载赋值(In[79]:= a = myType[1, 2, 3];
a = newValue;
a
Out[81]= newValue
运算符),这样它就可以处理分配给对象的变量你介绍的一些特定类型。这是一次尝试:
Set
这似乎有效。但是,让我们试试:
a
显然,它没有做我们想要的。问题是ClearAll[a,myType,myCustomCode,newValue];
myType/:Set[var_,rhs_]/;MatchQ[var,_myType]:=myCustomCode;
TagSetDelayed::tagpos: Tag myType in (var_=rhs_)/;MatchQ[var,_myType]
is too deep for an assigned rule to be found. >>
保持其l.h.s.,所以在模式匹配发生时,它只有符号UpValues
,而不是它的值。而且因为我们无法将定义与标签更深层次地关联到表达式的第一级,所以以下内容也不起作用:
=
据我所知,Set
不能用来解决这个问题,这很遗憾,因为对于各种数据类型,使用通常的UpValue
语法和自定义分配代码会很方便。有关类似的讨论,请参阅例如this发帖。这种情况对于UpSet
并不是唯一的 - 对于任何包含您要用于基于TagSet
的定义的参数的函数,情况也是如此。
UpSetDelayed
和TagSetDelayed
,UpSet
和UpSetDelayed
值得一提的是,当您使用Clear[a,b];
Plus[a,b]^:=1;
?a
Global`a
a/:a+b:=1
?b
Global`b
b/:a+b:=1
或TagSet
时,所有标记在级别1获取其他定义(规则)。例如:
TagSetDelayed
与此相反,ClearAll[a,b];
a/:Plus[a,b]:=1;
?a
Global`a
a/:a+b:=1
?b
Global`b
和TagSet
更精确:
TagSetDelayed
根据我的经验,后一种行为通常更为可取,因此在大多数情况下,我更喜欢UpSet
或UpSetDelayed
而不是{{1}}或{{1}}。
答案 2 :(得分:8)
Rcollyer已经给出了一个很好的答案,但是当你使用UpValues
时,这是一个示例:当你定义一个特定的数据结构时,它有自己的Head
,并且您想要定义像算术这样的内置操作如何使用该结构。我曾经为timeSeries
数据结构执行此操作,例如,添加将匹配第一列中的日期,并在第二列中添加相应的值对。如果您没有使用UpValue
定义此类操作,则在第一列中添加带有日期的T * 2向量会产生无意义的日期。