除了@rcollyer 的出色回答,我想强调一些关于UpValues 的其他重要事项。
系统和其他功能的软/本地重新定义
一个非常重要的方面是它们允许您仅在某些符号上“软”重载某些系统功能。 @rcollyer 指出了这一点的重要性,但怎么强调都不为过——这会使您的代码的效果局部化,并大大降低您的代码可以全局交互并影响系统其他部分或其他部分的机会用户定义的代码,不像你 Unprotect 系统符号并添加一些 DownValues 到它们。
除了安全和本地化之外,如果使用yourSymbol/:f_[_yourSymbol,rest___]:=... 之类的结构,这种重新定义也可能非常通用。这些应该谨慎使用,但有时可以提供非常简洁和简单的解决方案。 Here 是一个示例,其中一个代码可用于一次“重载”多个系统功能,从而为它们提供额外的重要功能。
评估顺序
下一点是评估。您可以遇到的常见语句是“UpValues 在DownValues 之前应用”。必须澄清这一点:对于f[g[args]],这意味着UpValues for g 在DownValues for f 之前应用,前提是评估过程已经一直“向下”到最里面的部分,然后去支持“向上”。特别是,这并不意味着UpValues for g 将在DownValues for g 之前应用-如果g[args] 可以在f 内部求值,因为g 具有适当的DownValues,它将(除非 f 具有Hold-attributes 之一),并且UpValues 的存在不会阻止这种情况,因为(对于标准评估),g[args] 的评估发生在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
g 的UpValues 没有机会申请,因为g[y] 在之前的评估步骤中被转换为Cos[y]。对于非标准评估,情况会有所不同——如果我们给f 属性HoldAll 或HoldFirst,或者如果我们将g[y] 包装在Unevaluated 中——在这两种情况下,我们都会给评估者一个跳过的指令g[y]的评价:
In[63]:= f[Unevaluated[g[y]]]
Out[63]= Sin[Cos[y]]
转义持有属性
这与前一点有关:应该知道,即使在具有Hold- 属性的头部内部也会执行搜索UpValues,因此,即使看起来相似,基于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]]}
一级标签深度限制
@rcollyer 已经提到了这一点。引入此限制是为了提高模式匹配器/评估器的效率。我只想强调它的一个重要且相当不明显的后果:看起来您不能使用UpValues 来重载赋值(Set 运算符),以便它可以处理分配给您的某些特定类型的对象的变量介绍。这是一个尝试:
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 拥有它的 l.h.s.,所以当模式匹配发生时,它只有符号 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. >>
据我所知,UpValues 不能用来解决这个问题,这很遗憾,因为使用通常的 = 语法和各种数据类型的自定义分配代码会很方便。有关类似的讨论,请参见例如this 帖子。这种情况对于 Set 来说并不是唯一的 - 对于任何包含您要用于基于 UpValue 的定义的参数的函数来说都是如此。
UpSet 和 TagSet、UpSetDelayed 和 TagSetDelayed 之间的一些区别
值得知道的是,当您使用UpSet 或UpSetDelayed 时,级别1 的所有 标签会获得额外的定义(规则)。例如:
Clear[a,b];
Plus[a,b]^:=1;
?a
Global`a
a/:a+b:=1
?b
Global`b
b/:a+b:=1
与此相反,TagSet 和 TagSetDelayed 更精确:
ClearAll[a,b];
a/:Plus[a,b]:=1;
?a
Global`a
a/:a+b:=1
?b
Global`b
根据我的经验,后一种行为通常更可取,因此在大多数情况下,我更喜欢TagSet 或TagSetDelayed 而不是UpSet 或UpSetDelayed。