“upvalue”在Mathematica中意味着什么以及何时使用它们?

时间:2011-07-11 09:37:31

标签: wolfram-mathematica

对我而言,g /: f[g[x_]] := h[x]只是等同于f[g[x_]] := h[x]。你能提出一个你必须使用/:的例子吗?

3 个答案:

答案 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将是不明确的。规则与aReals相关联的内容是什么?另外,如果我们想要一个正实数,我们可以设置Arg[#]:=0Simplify允许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

Level-1标签深度限制

@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的定义的参数的函数,情况也是如此。

UpSetDelayedTagSetDelayedUpSetUpSetDelayed

之间存在一些差异

值得一提的是,当您使用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

根据我的经验,后一种行为通常更为可取,因此在大多数情况下,我更喜欢UpSetUpSetDelayed而不是{{1}}或{{1}}。

答案 2 :(得分:8)

Rcollyer已经给出了一个很好的答案,但是当你使用UpValues时,这是一个示例:当你定义一个特定的数据结构时,它有自己的Head ,并且您想要定义像算术这样的内置操作如何使用该结构。我曾经为timeSeries数据结构执行此操作,例如,添加将匹配第一列中的日期,并在第二列中添加相应的值对。如果您没有使用UpValue定义此类操作,则在第一列中添加带有日期的T * 2向量会产生无意义的日期。