Aug 13, 2004

Mathematica:A world view


Forrest Sheng Bao http://fsbao.net
这几天因为准备CUMCM所以把Mathematica复习了一遍,感触颇多,加上许多人在学习的时候常问的一些问题,今天早上思如泉涌,没吃中饭,写下此文,还望大家一起讨论,批评 指正
Mathematica:A world view .
学习一种数学软件,并不仅仅是说我要知道它提供了哪些数学函数来完成数学计算(不论是数值的还是符号的),那样的话,计算机仅仅变成了一种计算器,只不过用语句代替了按键,只不过多了几个按键,只不过扩充了计算的范围而已,我们并没有从思想上去领会他的精妙之处。计算方法是一门学问,如何用更快的算法,用更精确的算法,不同的软件之间也许有差异,但我想不同的数学软件之间的差别在更大程度上是他们对数据的操作方式,如何看待不同类型的数据,甚至包括他们看待问题的角度,他们对数学概念的扩充。
Mathematica是一个符号运算系统,但是从纯数学的角度来说,其很多的符号运算能力在数 学中好像找不到对应的影子,不过如果把Mathematica看作一个具有一定局限性的程序设计语言,还是很好的。因为Mathematica的符号运算能力在一定程度上非常有利于我们用简单 的途径来实现对数学问题的求解。
Mathematica 的最精妙之处在于其看待表达式和函数(在谈到Mathematica的时候我们更习惯于称之为变换规则)的方式,至少在我目前看来是这样。当然Mathematica中表的概念也是令我感到耳目一新的,我曾经一度惊讶于Mathematica中矩阵仅仅是表的MatrixForm。表的概念仅仅是一种数据结构,而表达式和变换规则则是反映Mathematica思想的地方。
在mathematica中我们首先要体会到 []和()的区别。[]反映的是一种对应关系,而()只是 一个描述运算等级关系的运算符。这个区分是非常有必要的,在数学中()的使用其实是有歧异性的,比如3(a+b)我们都知道是3×(a+b),那么f(x+y)是f×(x+y)还是说把x+y作为函数f的变量带入f的表达式中进行计算呢?当然,按照我们的日常习惯我们会选择后一种理解。在Mathemaica中这种歧异被排除了,假使你采用了[],比如f[x+y],那么f[x+y]的值就是由定义f[x+y]的表达式和x与y的取值来决定,相反,如果你采用了f(x+y),系统则会默认的采用前一种解释,即f×(x+y)。当然你不妨实验一下,如果你给f(x+y)赋值,比如f( x+y)=x+y,或者赋以常值f(x+y)=3,你都会发现系统返回一个告警:
Set::write: Tag Times in f (x+y) is Protected.
这里牵涉到表达式的“头”的问题,我们在后面再说为什么会出现这个告警。
你可以看到系统按照f×(x+y)的理解去计算,不妨实验一下,下面的代码:
Clear["Global`*"]
f (x+y)=x+y
x=2
y=3
f=5
f(x+y)
就检验我上面说的话了,你同时还会发现这种先给变量赋值,再计算表达式值的方法不适 合我们所习惯的对函数的操作。
不过即便采用了f[x+y]的形式,你还会发现有很多不爽,试一试下面的代码:
Clear["Global`*"]
f[x+y]=x+y
f[2+3]
你会发现f[2+3]执行的结果居然是f[5],而非5。
试试像前一次实验一样,在语句f[x+y]=x+y之前先写下语句x=2和y=3,结果如何呢?你发现结果f[x+y]居然已经不是一个含有自变量的表达式了,而是一个实数了。也就是说除非重新对x和y赋值,否则f[x+y]的值就永远是5了。有人就急了,你不是说用[]可以定义 函数的吗?这哪里是函数啊?
考虑一下为什么会发生上面的怪事。因为在Mathematica中,“=”其实是赋值运算符,不 能像我们平时在数学中那样用于定义函数,这里我们就要介绍在Mathematica中表示定义的 符号“:=”,近似于我们在数学中经常使用“≡”来定义一样。
试一试下面的语句:
Clear["Global`*"]
f[x+y]:=x+y
f[2+3]
x=2
y=3
f[x+y]
f[2+3]
f[3+3]
结果你会发现不仅计算不出任何值来,比如最后两个语句的结果是f[5]和f[6],而且就算 为自变量赋值还是不能进行计算,4,5,6条语句的执行结果居然还是f[5]。
所以我们就会发现即使使用了定义符号还是不能像在数学中使用函数那样,因此我们就要考虑一下Mathemaica是如何进行函数值的计算了。
函数的本质是一种映射,所以如果要计算函数的值,不仅要有自变量的值,还要告诉计算机这些自变量按照什么样的规则来组成函数。因此,在Mathematica中,有专门的语句来定 义规则。
定义规则最常用的方法是在自变量的后面加上一个下划线“_”,在Mathematica中这个符号 被称为空白。
比如我们可以这样可以定义常函数f[_]=3,然后无论你在[ ]中输入什么数字,输出的结果 都是3。
你还可以通过下面的例子体会一下如何定义规则。
Clear["Global`*"]
f[x_,y_]=x+y^2
f[2,4]
这样,就和我们在数学中计算函数的值没有区别了吧。
但是把表达式声明为规则有一个不好,那就是无法把它看作一个可以进行各种符号运算的 表达式了,假使你要执行下面的语句
D[f[x_,y_],x]
是执行不起来的。
(当然我们在后面会指明f[x_,y_]只是定义规则所用的表达式,实际的规则可以通过f[x, y]来调用,这样还是可以实现符号运算的)
也就是符号运算和数值运算是无法同时兼顾的,所以我一般喜欢使用这 种语句,例如
Clear["Global`*"]
f=x^y
D[f,x]
f /.{x->3,y->5}
f
代码执行完以后,f依旧是一个含有变量的表达式。
这样就两全其美了,不过这种情况好像也不是很常遇到,所以规则的定义在更多的时候是 便于我们计算函数的值运算的,因为其形式是非常相似于我们在数学中所使用的形式。
回到规则定义上,我们会发现规则的定义不仅依赖于所使用的名称,同使用的参数也是有 关的,不论是参数的类型,数量。
看一下下面的例子
Clear["Global`*"]
f[x_]=x^2;
f[x_,y_]=(5+x)*y;
f[2]
f[3,6]
f[2]和f[3,6]分别调用不同的函数来进行计算。
这时你想到了什么?
如果你学习过C++语言,你就会想到这是函数的重载,也就是说,即使函数名相同,只要参 数的数量,类型,顺序不同,在同一个函数名之下的函数是不同的。这也正是OOP的多态性 的体现。
假如我们考虑一下Mathematica自己所提供的大量库函数,大部分都是重载函数,他们的参数表是可以不一样的。你自己到Mathematica的help中去随便找一找,同一个函数具有多种语法形式,以适用于不同的操作对象和操作目的。再考虑开去,在Matlab中也是这样的。
所以,数学软件同计算器的又一大不同就是,他们的运算符是具有多态性的,而计算器是 没有重载函数这个概念的。
记于2004年8月13日 13:48