Set(=)和SetDelayed(:=)之间有什么区别?

12 wolfram-mathematica function assignment-operator

这个讨论出现在之前的问题中,我很想知道两者之间的区别.举例说明会很好.

Mr.*_*ard 16

基本例子

以下是Leonid Shifrin的书Mathematica编程的一个例子:高级介绍

对于这类问题,它是一个很好的资源.见:(1) (2)

ClearAll[a, b]

a = RandomInteger[{1, 10}];

b := RandomInteger[{1, 10}]
Run Code Online (Sandbox Code Playgroud)
Table[a, {5}]
  {4, 4, 4, 4, 4}
Table[b, {5}]
Run Code Online (Sandbox Code Playgroud)
  {10, 5, 2, 1, 3}

复杂的例子

上面的示例可能给人的印象是,一旦使用创建符号的定义Set,其值就是固定的,并且不会改变.事实并非如此.

f = ...分配给f一个表达式,因为它的计算结果在分配时.如果符号保留在该计算表达式中,并且稍后它们的值发生变化,则表观值也会发生变化f.

ClearAll[f, x]

f = 2 x;
f
Run Code Online (Sandbox Code Playgroud)
  2 x
x = 7;
f
Run Code Online (Sandbox Code Playgroud)
  14
x = 3;
f
Run Code Online (Sandbox Code Playgroud)
  6

记住规则如何在内部存储是很有用的.对于赋值为的符号symbol = expression,规则存储在OwnValues.通常(但不总是),OwnValues只包含一个规则.在这种特殊情况下,

In[84]:= OwnValues[f]

Out[84]= {HoldPattern[f] :> 2 x}
Run Code Online (Sandbox Code Playgroud)

现在我们的重要部分是rhs,它包含x作为符号.对评估真正重要的是这种形式 - 规则在内部存储的方式.只要x在指定的时刻没有一个值,都SetSetDelayed生产(创建)以上的全球规则库相同的规则,这是最重要的.因此,它们在这方面是等同的.

最终结果是f具有类似函数行为的符号,因为其计算值取决于当前值x.然而,这不是真正的函数,因为它没有任何参数,并且仅触发符号的更改x.通常,不应该使用这样的结构,因为对于全局符号(变量)的隐式依赖在Mathematica中与在其他语言中一样糟糕 - 它们使代码更难理解,并且错误更容易被忽略.可以在这里找到一些相关的讨论.


用于功能的设置

Set可用于功能,有时需要.让我给你举个例子.这里Mathematica象征性地解决了Sum,然后将其分配给aF(x),然后将其用于绘图.

ClearAll[aF, x]

aF[x_] = Sum[x^n Fibonacci[n], {n, 1, \[Infinity]}];

DiscretePlot[aF[x], {x, 1, 50}]
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

另一方面,如果您尝试使用,SetDelayed则将每个值传递给Sum函数.这不仅会慢很多,但至少在Mathematica 7上,它完全失败了.

ClearAll[aF, x]

aF[x_] := Sum[x^n Fibonacci[n], {n, 1, \[Infinity]}];

DiscretePlot[aF[x], {x, 1, 50}]
Run Code Online (Sandbox Code Playgroud)

如果要确保形式参数的可能全局值(x此处)不会干扰并在定义新函数的过程中被忽略,则替代方法Clear是包围Block定义:

ClearAll[aF, x];
x = 1;
Block[{x}, aF[x_] = Sum[x^n Fibonacci[n], {n, 1, \[Infinity]}]];
Run Code Online (Sandbox Code Playgroud)

查看函数的定义可以确认我们得到了我们想要的东西:

?aF
Global`aF
aF[x_]=-(x/(-1+x+x^2))
Run Code Online (Sandbox Code Playgroud)


rag*_*eld 9

In[1]:= Attributes[Set]

Out[1]= {HoldFirst, Protected, SequenceHold}

In[2]:= Attributes[SetDelayed]

Out[2]= {HoldAll, Protected, SequenceHold}
Run Code Online (Sandbox Code Playgroud)

正如你可以通过它们的属性看到的那样,两个函数都拥有它们的第一个参数(你要分配的符号),但它们的不同之处在于SetDelayed也保存了它的第二个参数,而Set则没有.这意味着Set将=在分配时评估表达式的右侧.:=在实际使用变量之前,SetDelayed不会计算右边的表达式.

如果作业的右侧有副作用(例如Print []),发生的事情会更清楚:

In[3]:= x = (Print["right hand side of Set"]; 3)
x
x
x

During evaluation of In[3]:= right hand side of Set

Out[3]= 3

Out[4]= 3

Out[5]= 3

Out[6]= 3

In[7]:= x := (Print["right hand side of SetDelayed"]; 3)
x
x
x

During evaluation of In[7]:= right hand side of SetDelayed

Out[8]= 3

During evaluation of In[7]:= right hand side of SetDelayed

Out[9]= 3

During evaluation of In[7]:= right hand side of SetDelayed

Out[10]= 3
Run Code Online (Sandbox Code Playgroud)