Mathematica入门(二)原则2:模式匹配和规则替代
第二条原则:模式匹配和规则替代
另一条基本原则是所谓模式匹配。它是将规则与表达式进行匹配的一个系统。离开这个系统,Mathematica 将无法知道什么时候使用哪一条规则。给系统是按照语法而非语义在表达式间做比较。我们这里的话题主要是和规则与模式相关的。
2.1 重写规则
一个典型的规则具有如下的形式:
a -> b
其中,a 和 b 为 Mathematica 表达式。规则的作用是,一旦遇到 a ,就将其用 b 来替代。 例如:
In[X]:= {a, c, d, c}/. a -> b
Out[X]:= {b, c, d, c}
(这里,</.> 是规则替代命令,将在下面的章节中介绍)。
模式可以是任何一个某一部分被"blank"(Blank[])所取代的表达式,这里Blank[]代表的是任意表达式的占位,也就是说这一部分可以被任何东西所取代(这多少有点过度简化)。与Blank[]等价的是单下划线("_")符号。比如,f[x_]意思是f[任何东西]。
2.2 一个简单的模式定义的函数的例子
In[X]:= Clear[f]
In[X]:= f[x_]:=x^2;
In[X]:= {f[2],f["wood"],f[Newton]}
Out[X]:= {4,word^2,Newton^2}
在这个例子中,产生的结果遵循了函数f的定义,即f[任何东西]被替换成(任何东西)^2。
2.3 函数就是规则:DownValues命令
要看清这个规则的内部形式——即它是如何存储到规则库里的——我们可以借助内置的DownValue命令。在它的帮助下我们可以看到:
In[X]:= DownValue[f]
Out[X]:= {HoldPattern[f[x_]]:>x^2}
HoldPattern函数的作用我们会在后面提到。这里的模式x_是最简单的一种模式。模式可以变得很复杂,不仅仅是语法层面上,模式还可以附加条件,这样就可以使得在条件得到满足的前提下模式才能得到匹配,这种模式又称条件模式。这一点我们会在之后的章节里详细提到。
2.4 一个基于限制模式的函数的例子
现在来看一个例子:我们限制函数f只能作用在整数上:
In[X]:= Clear[f];
In[X]:= f[x_Integer]:=x^2;
In[X]:= {f[2],f[Pi],f["word"],f[Newton]}
Out[X]:= {4,f[Pi],f[word],f[Newton]}
在这个例子中,我们引入了一个稍复杂的模式x_Integer。
2.5 关于求值
在这个例子中,我们可以看到如果规则中的模式部分(规则等号左边的那部分)未能匹配到任何表达式的话,Mathematica 就会原封不动地返回这个表达式。这牵涉到它的求值方法的一个核心部分:对于任何输入的表达式而言,在当前时刻全局规则库中的所有规则会反复作用到表达式上。当其中某条规则作用到表达式上后,表达式会被重写,之后整个过程再次重复。到某个时候,当没有一条规则能够再作用到表达式上后,此时的表达式就是最终结果。由于规则库同时包括系统内置规则和用户自定义规则(后者有更高的优先级),因而我们在操纵表达式上更加得心应手。
2.6 通过模式给同一个函数添加多个定义
现在我们来看另一个简单的例子,我们定义一个函数f,它对于偶数是线性的,对于奇数是二次的,对于剩下的其他数字则表示为正弦函数:
In[X]:= Clear[f];
In[X]:= f[x_?EvenQ]:=x;
In[X]:= f[x_?OddQ]:=x^2;
In[X]:= f[x_]:=Sin[x];
下面是这个函数在不同输入下的输出结果:
In[X]:= {f[1],f[2],f[3],f[4],f[3/2],f[Newton],f[Pi]}
Out[X]:= {1,2,9,4,Sin[3/2],Sin[Newton],0}
在这里,内置函数OddQ和EvenQ是一个判定函数,前者对输入是奇数的情况返回True,对输入是偶数返回False:
In[X]:= {EvenQ[2],EvenQ[3],OddQ[2],OddQ[3]}
Out[X]:= {True, False, False, True}
如果输入是一个不明确的东西,返回值将会是False:
In[X]:= EvenQ[Newton],OddQ[Newton]
Out[X]:= {False, False}
2.7 规则替换的不可交换性
现在我们来看一下上面这个例子中函数f三个定义中的最后一个。它的意思是所有的输入都会变成它的正弦值。起初我们会以为我们会得到所有输入的正弦值作为输出,但这种情况并没有发生。这是因为顺序式的规则应用是不具有交换性的:首先,在每一次规则应用的过程中,一旦找到第一条规则可以匹配的部分后,只有这一条规则会被应用,而其余的规则即使可以匹配也不会被应用到(子)表达式上。其次,如果有很多规则都能匹配同一个表达式,那么只有排在第一位的规则会改写表达式,这样的结果是其他(一些)表达式不再能匹配这一被改写后的表达式。因此我们可以看出输出结果取决于规则被应用的先后顺序。Mathematica 会依次应用规则。由于正弦的定义排在最后,这意味着它仅仅能够作用于那些不满足前两条规则的输入。
2.8 规则自动重排
如果我们把上一个例子中的规则定义的顺序换一下,得到的仍然是相同的结果:
In[X]:= Clear[f];
In[X]:= f[x_]:=Sin[x];
In[X]:= f[x_?EvenQ]:=x;
In[X]:= f[x_?OddQ]:=x^2;
In[X]:= {f[1],f[2],f[3],f[4],f[3/2],f[Newton]}
Out[X]:= {1, 2, 9, 4, Sin[3/2], Sin[Newton]}
为了了解规则储存的顺序,我们再次用到DownValues函数:
In[X]:= DownValues[f]
Out[X]:= {HoldPattern[f[x_?EvenQ]]:>x, HoldPattern[f[x_?OddQ]]:>x^2, HoldPattern[f[x_]]:>Sin[x]}
我们发现尽管我们第一个定义正弦规则,但它仍然排在最后。这是因为Mathematica 规则替换引擎用内置的规则分析器来给规则排序,更一般的规则被排在更细致的规则之后,前提是分析器要能够判断哪个规则更一般。但这一般来说不可能自动完成,更别说要消除歧义,因而编程者应当注意这一点。但在实际应用中,很少需要手动给规则排序。