【问题标题】:What does "upvalue" mean in Mathematica and when to use them?Mathematica 中的“upvalue”是什么意思以及何时使用它们?
【发布时间】:2011-10-02 15:48:54
【问题描述】:

对我来说,g /: f[g[x_]] := h[x]f[g[x_]] := h[x] 完全等价。你能举一个你必须使用/:的例子吗?

【问题讨论】:

标签: wolfram-mathematica


【解决方案1】:

其实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.

我们可以删除UnprotectPlus,然后我们的定义就会起作用,但这可能会导致其他定义出现问题,并且随着Plus 累积更多定义,它会变慢。或者,我们可以通过TagSet 将addition 属性与mod 对象本身相关联

mod /: mod[a_] + mod[b_] := mod @ Mod[a + b, 5]

UpSetDelayed

mod[a_] + mod[b_] ^:= mod @ Mod[a + b, 5]

从概念的角度来看,设置upvalue 更正确,因为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 )

a_ \[CirclePlus] b__ := BlockDiagonal[{a,b}]

为我经常使用的矩阵运算提供方便的简写符号。

我上面的例子有点做作,但有很多次UpValues 派上用场了。例如,我发现我需要一个符号形式来表示在乘法和幂运算下表现适当的复单位根。

示例:一个简单实用的示例是将Symbol 标记为真实:

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[#]:=0,它允许Simplify 的行为符合预期,例如Simplify[Sqrt[a^2]] == a

【讨论】:

  • 示例不应该类似于mod /: a_mod + b_mod := mod@Mod[Delete[a, 0] + Delete[b, 0], 5]吗?否则我会遇到递归错误。也许我错过了一些东西,试图把我的头包裹在 upserts 上。
  • 或者更好mod /: a_mod + b_mod := mod@Mod[Identity@@a + Identity@@b, 5]
  • @gleno 很好。我认为更好的选择是使用mod[a_] + mod[b_],更简单。当时,我想我很喜欢在模式中使用Head(这很整洁),但如果你需要使用内部结构,它就不太有用了。
【解决方案2】:

除了@rcollyer 的出色回答,我想强调一些关于UpValues 的其他重要事项。

系统和其他功能的软/本地重新定义

一个非常重要的方面是它们允许您仅在某些符号上“软”重载某些系统功能。 @rcollyer 指出了这一点的重要性,但怎么强调都不为过——这会使您的代码的效果局部化,并大大降低您的代码可以全局交互并影响系统其他部分或其他部分的机会用户定义的代码,不像你 Unprotect 系统符号并添加一些 DownValues 到它们。

除了安全和本地化之外,如果使用yourSymbol/:f_[_yourSymbol,rest___]:=... 之类的结构,这种重新定义也可能非常通用。这些应该谨慎使用,但有时可以提供非常简洁和简单的解决方案。 Here 是一个示例,其中一个代码可用于一次“重载”多个系统功能,从而为它们提供额外的重要功能。

评估顺序

下一点是评估。您可以遇到的常见语句是“UpValuesDownValues 之前应用”。必须澄清这一点:对于f[g[args]],这意味着UpValues for gDownValues 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

gUpValues 没有机会申请,因为g[y] 在之前的评估步骤中被转换为Cos[y]。对于非标准评估,情况会有所不同——如果我们给f 属性HoldAllHoldFirst,或者如果我们将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 的定义的参数的函数来说都是如此。

UpSetTagSetUpSetDelayedTagSetDelayed 之间的一些区别

值得知道的是,当您使用UpSetUpSetDelayed 时,级别1 的所有 标签会获得额外的定义(规则)。例如:

Clear[a,b];
Plus[a,b]^:=1;

?a
Global`a
a/:a+b:=1

?b
Global`b
b/:a+b:=1

与此相反,TagSetTagSetDelayed 更精确:

ClearAll[a,b];
a/:Plus[a,b]:=1;

?a
Global`a
a/:a+b:=1

?b
Global`b

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

【讨论】:

  • +1,一如既往地提醒我TagSetTagSetDelayed 是三元运算符。
  • 虽然它没有像预期的那样工作,但我已经能够超载SetDelayedpreviously。啊,我明白了,为什么我专门使用符号,而不是它们的值,所以限制不成立。
  • @rcollyer 你可以用UpValues 在显式符号上重载SetDelyaed,这是真的。我自己并没有完全探索这种可能性,但很可能可以以建设性和有趣的方式使用它。 @WReach 最近的回答就是这样一个例子:stackoverflow.com/questions/6917656/…
  • 我没有看过那个问题。有趣的方法。
  • @Leonid Re:评估 f[g[arg]] 时的评估顺序是:g 的向下值,g 的向上值,f 的向下值。 g 的下限值在 g 的上限值之前进行评估。但是,如果您查看 ?g,您会看到上值列在下值之前。所以演示顺序有点误导。你同意吗?
【解决方案3】:

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

【讨论】:

  • 我经常使用makeReal。不过,我通常必须每次都重写它,因为它是微不足道的。但是,更有用的是,我已经完成了与您使用的程序相同的操作,因为我使用的程序会产生自旋密度,我想要总密度(自旋向上 + 自旋-dn)和磁化强度(自旋向上 - 自旋-dn),并且对象表示并不简单。
猜你喜欢
  • 2011-04-23
  • 2012-07-22
  • 2012-09-12
  • 1970-01-01
  • 2010-09-16
  • 2010-11-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多