跳至內容

Mathematica/初級操作

維基教科書,自由的教學讀本

引言

[編輯]

在這一章我們介紹基本的變量操作(如賦值),還有一些過程式控制結構(如條件和循環)。

符號和變量

[編輯]

在Mathematica里,變量其實就是一些能儲存定義的符號。更精確的說,變量就是不含模式的全局規則的左值(這是我們對變量的定義,下面會詳細介紹)。

合法的符號名稱

[編輯]

一個合法的符號名稱必須滿足一下幾個要求:

  • 不以數字開頭(可以包含數字)
  • 不能使用特殊字符(@,#,$,%,^,&,*,!,~,`等等)
  • 不能使用下劃線"_"
  • 可以使用希臘字母,甚至漢字

註:Mathematica是對大小寫敏感的,<function>和<Function>被區別對待。 如果不是很清楚一個符號名是否是合法的可以用下面的方法判斷:

Head[Unevaluated[你的符号名]]

如果得到的結果是Symbol,那麼這個符號名就是合法的[1]。 另外提到一點,在定義符號的時候最好使用小寫字母開頭(以大寫字母開頭是完全可以的),因為所有的內置符號都是以大寫字母開頭的。使用小寫字母開頭的符號名可以一勞永逸的消除「撞車」的情況。

直接變量和OwnValues

[編輯]

直接變量("Proper" variables)[2]就是一類直接用來儲存表達式的符號。被存儲的這個表達式的類型沒有任何限制,可以是原子也可以是普通表達式。(實際上,Mathematica列根本就沒有類型的概念) 接下來介紹函數OwnValues,這個函數用來查看直接變量被賦的值。比如:

In[]:=  a = 3;
        OwnValues[a]
Out[]=  {HoldPattern[a]:>3}

這裏的等於號表示的是賦值操作符,下面會詳細介紹。

這裏我們就有了「直接變量」的另一種定義,即它們的值是存儲在OwnValues里的。一個符號的OwnValues只能存儲0個或1個規則,而且這裏所存儲的規則必須是直接和符號(擁有頭部Symbol的表達式)掛鈎的,這就是稱這種變量為「直接變量」的原因。

索引變量和DownValues

[編輯]

除了上面的方法,還有另外一種存值的方法,這種方法有時候也被當作某種形式的變量看待。下面是個具體的例子:

b[1] = 1;
b[2] = 4;
b[3] = 9;

這看起來很像是索引,其實不是的,真正的索引操作符是[[ ]]。我們可以來檢查一下這裏的定義是否存儲在OwnValues里:

In[]:=  OwnValues[b]
Out[]=  {}

嗯,看起來沒有。其實,上面的賦值過程所建立的規則被存儲在DownValues(另一種全局規則)里:

DownValues[b]
{HoldPattern[b[1]]:>1,HoldPattern[b[2]]:>4, HoldPattern[b[3]]:>9}

可以看到,在同一個符號<b>的身上,被聯繫了好多條規則,都存儲在<b>的DownValues里。所以,一種解釋我們上面賦值過程的說法是:我們定義了一個函數<b>,這個函數的參數是三個明確的值1、2、3;然而,有時把b[n]看成是一種「索引變量」似乎更加方便。因為這裏對b的定義沒有用到模式,只是類似於索引的三個數字而已。

索引變量在某些情況下是很有用的。這裏的「索引」還可以擴展為其它的類型,比如字符串,這樣實現類似於Python里的「字典」的數據結構就成為可能。在Mathematica內部,哈希表被用來保證索引變量訪問時的高效率。所以,雖然Mathematica沒有直接提供哈希表的支持,仍然可以通過一些變相的方法(比如這裏的索引變量)來使用內部的哈希表。

複合變量和SubValues

[編輯]

基本不會用到,暫不譯。

變量的清除

[編輯]

被綁定在變量和函數上的規則都存儲在全局規則庫里,一個「清除」命令就顯得十分重要了。這個可以通過Clear命令實現。Clear命令的作用是清除一個符號的所有定義,包括OwnValues、DownValues、SubValues等等。在使用一個符號之前清除它的定義是個好習慣。下面會給出許多例子,先來個簡單的。

可以用<?>來查看當前<b>的定義:

?b
Global`b
b[1] = 1
b[2] = 4
b[2] = 9

現在執行Clear[b]:

Clear[b];
?b
Global`b

可以看到所有的定義都被清除了,但是符號<b>仍然存在於符號表(symbol table)里。

在這裏需要注意的是,被Clear的對象只能是符號或者字符串[3]。下面嘗試就失敗了:

Clear[a[b]]
Clear::ssym: a[b] is not a symbol or a string.>>

在這種情況下,可以使用Unset(簡寫成=.)來清除特定的值。可以理解為給一個對象賦<.>就能把它的值清除了。具體用法詳見幫助。

Clear命令能清除一個與一個符號綁定的所有規則,但是還有一些其它的性質是不能被清除的,比如選項(Options)和屬性(Attributes)。看看下面的代碼:

Options[b] = {Heads -> True};
Attributes[b] = HoldAll;

我們已經給<b>賦了一些性質,現在我們Clear一下:

Clear[b]
?b
Global`b
Attributes[b] = {HoldAll}
Options[b] = {Heads->True}

剛才賦的選項和屬性都還在,要清除這些東西,可以用更厲害的ClearAll:

ClearAll[b];
?b
Global`b

不過,ClearAll還不是終極Boss,被ClearAll的符號還是沒有完全消失(仍然在符號表內)。如果想把一個符號完全抹去,你需要Remove命令:

Remove[b];
?b
Information::notfound : Symbol b not found.>>

檢測一個符號是否有值 ValueQ

[編輯]

不同於其它語言,在Mathematica里,沒有值的變量(就是一個無意義的符號)是完全合法的,為了檢查一個變量是否有值,可以使用函數ValueQ。[4]

小結

[編輯]
  • Mathematica中的變量可以是符號也可以是普通表達式,能用來存儲任意的Mathematica表達式。
  • 符號名稱可以包含字母或數字,但是不能以數字開頭或包含特定的特殊符號,包括下劃線。
  • 可以用Head[Unevaluated[符號名]]來檢查一個符號名是否合法。
  • 符號名不得與內置符號相衝突,使用小寫字母打頭的符號名可以避免衝突。
  • <?>和<Information>可以用來查閱一個符號的信息。
  • 變量是用全局規則實現的。直接變量用OwnValues實現,索引變量用DownValues實現。
  • Clear用來清除變量上的規則;ClearAll在清除規則的同時清除選項和屬性;Remove可以徹底清除一個符號;Unset用來清除一條特定的規則。

動態數據類型

[編輯]

Mathematica是一門動態類型語言,這意味着一個變量的使用是無需事先聲明的,而且同一個變量可以作為多種數據類型的佔位符(並不推薦這樣做)。事實上,這裏根本沒有傳統程式語言里的那種類型的概念,類型的概念被原子和普通表達式替代。

任何新輸入的對象都被當作一個符號實體。每當新的符號被輸入,這個符號就被符號表檢索。如果這個符號在符號表中已經存在,該符號的一個新的引用(reference)就被建立。如果不存在,一個新的條目就在符號表中被創造。如果這個對象是新的,或者沒有能馬上應用到它身上的規則,這個符號就被原樣返回:

In[]:= Clear[a];
       a
Out[]= a
In[]:= Sin[a]
Out[]= Sin[a]

在最後一個例子裏,Sin是一個內置符號,但是沒有能應用在Sin[a]或者a上的規則,所以Sin[a]被原樣返回了。

賦值

[編輯]

立即賦值與延時賦值:Set和SetDelayed

[編輯]

Mathematica里有兩種賦值命令:立即賦值和延時賦值。

立即賦值用等於號"="表示,x=y表示「立即將y的值賦給x」。與這個命令等價的完全形式是Set,Set[x,y]就和前面的寫法等價。":="用來表示延時賦值,比如x:=y。與":="等價的是SetDelayed,比如SetDelayed[x,y]。這表示「在全局規則庫里添加一條規則,每當遇到x就將其替換為y在當時的值」。所以,如果使用延時賦值,等號右邊的表達式在賦值的那一刻並不會被計算,而是在被賦值的表達式在程序的某一處出現時再重複計算。而Set在賦值那一刻計算就已經完成,這是兩種賦值操作符的主要區別。

Set與SetDelayed的區別:舉例說明

[編輯]

這是一個例子:

In[]:=  Clear[a,b];
        a = Random[Integer, {1,10}];
        Do[Print[a]],{5}]
Out[]=  2
        2
        2
        2
        2
In[]:=  b:=Random[Integer, {1, 10}];
        Do[Print[b],{5}]
Out[]=  2
        6
        6
        10
        2

在兩個例子中,一個符號(a或b)都被賦予了一個10以內的隨機數。在前一個例子中,這個隨機數在賦值時馬上被計算並與a聯繫起來,但是在後一個例子中求隨機數的函數在每次打印過程中都被重複計算。這裏我們用到了一個循環結構Do,稍後會介紹。

Set和SetDelayed:該用哪一個?

[編輯]

通常情況下,我們用Set(=)定義變量,用SetDelayed(:=)定義函數,因為函數通常要對不同的參數進行計算。然而,這裏體現出了Mathematica和其它語言的不同:函數和變量的區別不是用類似於<function>的關鍵字來達到的,而是通過定義符號的不同方法達到的。一方面是通過兩種不同的賦值操作符,另一方面是不同類型的全局規則(OwnValues和DownValues)。這就意味着函數和變量在Mathematica里可以同等看待。函數和變量之間沒有本質差別這個特性是函數式編程的核心。函數式編程是Mathematica里最高效的編程範式之一,後面的章節會頻繁使用。

檢查表達式是否相等

[編輯]

有兩個檢查相等的操作符,一個檢查語義(表達式的值)的是否相等,一個檢查表達式結構的是否相等。

Equal

[編輯]

第一個操作符和很多語言(比如C語言)的一樣,兩個連續的等於號"==",比如x==y(完全形式是Equal[x,y])。Equal支持鏈式檢查,所以類似於x==y==z(完全形式可以寫做Equal[x,y,z])的寫法是完全合理的(這一點就和C不一樣了)。Equal可以擁有多個參數,這一點很方便:

In[]:= Clear[a,b,c];
       a=b=c=2;
       d=3;
       {a==b, b==d, a==b==c, Equal[a,b,c],Equal[a,b,c,d]}
Out[]= {True, False, True, True, False}

注意:Equal有時會罷工!

[編輯]

Equal對任何Mathematica表達式都起作用,包括原子和普通表達式。然而,當Equal不能判斷兩邊的表達式是否有相同的值的時候就會原樣返回:

In[]:= Sin[x]^2 + Cos[x]^2 == 1
Out[]= Cos[x]^2 + Sin[x]^2 == 1

但是這並不意味着Mathematica不能化簡左邊的表達式:

In[]:= Simplify[Sin[x]^2 + Cos[x]^2]
Out[]= 1

上面的例子僅僅表明在默認的情況下Mathematica不會去化解左邊的那個式子。在這些情況下,Equal會以未計算的形式返回。這個特性在符號計算系統裏是很有用的,有時新的規則會添加,沒準這時Evaluate就能返回True或者False了:

In[]:= Simplify[Sin[x]^2 + Cos[x]^2==1]
Out[]= True

Equal被用來構建方程

[編輯]

上面說的特性(拿不準的時候返回原式)被很多內置的與解方程相關的函數利用,比如Solve,DSolve等等:

In[]:= Solve[x^2-3 x + 2 ==0, x]
Out[]= {{x->1},{x->2}}

把Equal的結果賦給變量 再議計算

[編輯]

就和其他的東西一樣,Equal[a,b]也是表達式,所以我可以把這個表達式的值賦給變量,看看下面的例子:

In[]:= Clear[x,test];
       test= Sin[x]==0
Out[]= Sin[x]==0

現在我們給x賦值,再來看看結果:

In[]:= x = Pi;
       test
Out[]= True

賦值之前test返回的是未計算形式,但是一旦x被賦值,表達式test被計算成True。讓人感到驚訝的是,此時test仍然擁有先前的定義,而不是變成了True:

?test
Global`test
test = Sin[x] == 0

特別地,如果我們此時把x的值Clear掉,結果又會變成符號表達式:

In[]:= Clear[x];
       test
Out[]= Sin[x] == 0

這意味着變量test事實上很像一個x的函數(這不是定義函數的合適方法!)。更重要的是,這向我們解釋了一部分計算的機制。首先,test的定義代表了全局規則庫中的一個規則,可以用OwnValues看到:

In[]:= OwnValues[test]
Out[]= {HoldPattern[test]:>Sin[x] == 0}

其次,通過Set添加的全局規則一旦被創建就不再改變——等於號右邊的表達式在賦值時就被計算並被保存到規則庫中。如果賦值的時候一些變量沒有值,那麼它們就會以符號的形式添加到規則庫中。即使後來這些無值變量被賦了值,先前通過Set創建的定義(全局規則)也不會改變(除非被手動清除或改變)。這就是為什麼在我們這個例子中test的定義總是保持不變,即使x的值在變化。

全局規則的狀態和系統的狀態是相互獨立的,意識到這一點非常重要。全局規則的添加或清除只能通過規則庫實現而與系統狀態的改變無關。仔細想一想,這也是唯一可能的結果,因為系統的狀態本身就是被全局規則庫決定的。所以,剛才的討論可以換個說法:在當前沒有發生計算的情況下,添加到規則庫中的若干條規則之間不會相互影響。如果不是這樣的話,規則庫里就會亂成一團,有的規則就會改變其它的規則!

幽靈般的副作用

[編輯]

呃,Mathematica的符號特色可能會讓人認為如果"=="兩邊的表達式完全一樣的話,結果肯定是True。事與願違,副作用總是會神出鬼沒地影響着表達式的結果。在下面這個例子裏,我們構建一個能使其參數「自增」的函數(不要理代碼,後面會介紹)。

Clear[a,inc];
a = 5;
inc[x_]:=x=x+1;
Attributes[inc]={HoldAll}

看看結果:

In[]:= inc[a]==inc[a]
Out[]= False

你看出發生什麼了嗎?

題外話:標準與非標準計算

[編輯]

很容易用Trace命令追蹤上面到底發生了什麼:

In[]:= a = 5;
       Trace[inc[a] == inc[a]]
Out[]= {{inc[a],a=a+1,{{a,5},5+1,6},a=6,6},{inc[a],a=a+1,{{a,6},6+1,7},a=7,7},6==7,False} 

我們能看到的是,首先左邊的inc[a]被計算,在得出結果6的同時導致了一個副作用(a的值被改變了)。然後右邊的inc[a]被計算,因為先前的a已經被改為6了,所以這次的結果是7,最後6==7得到False[5]

我用這個例子闡述以下幾點。首先,子表達式inc[a]是在外層的Equal之前被計算的——這是Mathematica中的通用順序,從葉到枝,從枝到干,從裏到外。則被稱作標準計算。然而,按照這個邏輯,上面例子中的a應該被首先計算啊?a在inc的裏層,難道不應該先被計算成5嗎?這是對的——正常情況下的確是這樣。但是這裏是個例外,a沒有被首先計算,這叫做非正常計算(在這裏是用HoldAll屬性做到的,後面會介紹)。

正常和非正常計算對於Mathematica的功能實現都非常重要。大部分的內置函數按照「正常」模式進行計算,但是還有一部分重要的函數按「非正常」模式計算。這個話題現在談太深入,我們以後會回頭來詳細介紹。

地址傳遞的模擬

這個例子還表明了另外一個觀點,我們有可能通過一個函數去「真正地」改變參數的值(正如上面的inc函數所做的)。這有點像C語言中的傳地址方式(由於Mathematica里沒有指針,所以這句話並不完全正確)。然而在通常情況下,類似的東西很少在Mathematica編程中使用,因為函數一般在參數的拷貝上進行操作,而並不改變原來的參數。Mathematica函數能返回任意表達式(包含很多結果的表),而且內存動態分配(變量無需聲明),模擬傳地址語義真的很沒必要!

SameQ

Equal的這些特性,尤其是「模凌兩可」(返回未計算形式)這個行為,有時可能讓人不太滿意——需要的是"非真即假"的答案。這裏還有另外一個操作符可以滿足我們的需求。

這個操作符是三連等號「===」,比如x===y,完全形式寫做SameQ,如SameQ[x, y]。SameQ比較的是表達式的「形」而非「義」。當表達式的形式上相同時返回True,否則返回False,再來看看前面的例子:

In[]:= Clear[x];
       Sin[x]^2 + Cos[x]^2 === 1
Out[]= False

有趣的是我們可以構造出Equal返回True而SameQ返回False的例子。比如下面這個:

In[]:= Pi/2 == Pi/2.
Out[]= True
In[]:= Pi/2 === Pi/2.
Out[]= False
這裏兩邊的值雖然是相等的,但是形式卻不一樣:

 In[]:= {Pi/2, Pi/2.}
 Out[]= {Pi/2, 1.5708}

但是,反過來說是對的:如果SameQ給出True,則Equal必然給出True。

TrueQ

SameQ有時還是讓人不太滿意,因為當Equal給出True的時候它可能得出False。比如下面的情況:

In[]:= Clear[a, b, c, d];
       a := Equal[c, d];
       b := SameQ[c, d];
       {a, b}
Out[]= {c==d, False}
In[]:= c=Pi/2;d=Pi/2.;
       {a,b}
Out[]= {True, False}

這意味着,作為Equal的替代品,SameQ可能與Equal有着微妙的區別(不像Equal,SameQ總是給出True或者False)。這時另一個傢伙就很方便了——TrueQ。這個函數在其參數不為True的時候給出False。所有,對於上面的例子,正確的做法應該是這樣:

In[]:= Clear[a, b, c, d];
       a := TrueQ[Equal[c, d]];

檢查結果:

In[]:= a
Out[]= False
In[]:= c = Pi/2; d = Pi/2.;
       a
Out[]= True
In[]:= Clear[a, b, c, d]

另外還補充兩個函數,從字面上就能看出是幹什麼的:

  • Unequal
  • UnsameQ

邏輯運算符

和其他語言一樣,Mathematica也有很多內置的邏輯運算符。我們介紹:

  • 邏輯與:And, 簡寫作"&&""
  • 邏輯或:Or,簡寫作"||"
  • 邏輯非:Not,簡寫作"!"

嗯,就如你看到的,這裏的幾個簡寫和C的寫法完全一樣。 這個算符返回True或False,在不能確定結果的時候也會原樣返回:

In[]:= And[a, b]
Out[]= a&&b

這是這類算符和其它語言的最主要不同點,主要歸因於Mathematica符號天性。當你需要確定的結果的時候,可以用TrueQ:

In[]:= TrueQ[And[a, b]]
Out[]= False

另外一點區別是And和Or可以有多個參數(C語言不能這樣):

In[]:= And[a, b, c]
Out[]= a&&b&&c
In[]:= Or[a, b, c]
Out[]= a||b||c

And和Or還有和C相仿的「短路」性質:And在遇到一個False之後馬上停止計數並返回False;Or在遇到一個True之後也馬上停止計數並返回True。一般情況下參數是先於函數計算的,所以我們可以下結論說And和Or都用的是非標準計算。

條件

If

If的語法如下:

  • If[test,oper1,oper2]

這裏的test是被檢驗的條件。原則上test的結果必須為True或False以便If做出選擇。如果結果為真,oper1被計算,否則計算oper2。oper2可以省略,這時如果test的結果是False 就什麼也不做並返回Null(代表「空」的關鍵字)。

你發現了嗎?If用的也是非標準計算。後面兩個參數不是在If之前計算的。

If也可能「罷工」

當條件test沒能返回True或False的時候,If也會原樣返回:

In[]:= Clear[a, b, c];
       If[a, b, c]
Out[]= If[a, b, c]

在這裏我們能用TrueQ來確保能夠進行,或者也可以利用If的第4個參數,這個參數在條件不能被明確判斷的時候被計算:

In[]:= If[a, b, c, d]
Out[]= d

If返回一個值

「此If」和「彼if」的另外一點區別是Mathematica中的If不但會計算相應的表達式而且還會將結果返回。這很類似於C中的條件表達式a?b:c。所以下面的代碼是有意義的:

In[]:= Clear[a, b, x, y];
       a:= If[EvenQ[b], x, y];

看一下結果:

In[]:= a
Out[]= y

EvenQ對於無法判斷的情況總是返回False,上面的結果也就能解釋了。

In[]:= b=2;
       a
Out[]= x

在試一次:

In[]:= b = 3;
       a
Out[]= y

注意到這裏a的定義用了延時定義SetDelayed(:=)。再舉個例子:

In[]:= Clear[a, x, y, test];
       a:= If[test, x, y];
       test := (Sin[x]==Cos[x]);
       a
Out[]= IF[Sin[x]==Cos[x],x, y]
现在:
In[]:= x=Pi/2
       a
Out[]= y
In[]:= x=Pi/4
       a
Out[]= Pi/4

Which和Switch

這兩個命令擁有多分枝選擇結構,可以代替嵌套的If。Switch和C語言裏的switch相似,但是還是有顯著的不同:Switch能配合模式一起使用;Break[]在此不起作用,所以C中fall-through現象就不能實現了(模式可以做一點補償)。C語言中,switch可作為不同代碼段的入口,在Mathematica里則不能。Which和Switch在幫助里說得很詳細,以後要用到的時候再查閱。

循環

Mathematica里循環結構主要的模仿對象是C,不過逗號和分號的作用正好互換過來了。循環在Mathematica里一般不是很高效,更重要的是函數式風格的編程通常根本不會用到循環。不過為了完整性,在這裏舉幾個簡單的例子。

我們的例子為:打印數字1-5。

For循環

In[]:= For[i=1, i<=5, i++, Print[i]];
Out[]= 1
       2
       3
       4
       5

While循環

In[]:= i=1;
       While[i<=5, (Print[i];i ++)];
Out[]= 1
       2
       3
       4
       5

Do循環

In[]:= Do[Print[i], {i,1, 5}]
Out[]= 1
       2
       3
       4
       5

循環引起的副作用

通常Mathematica的內置函數式不會引起副作用的,因為它們的操作的都是說給表達式的拷貝。然而在For和While循環中,循環結束後循環變量然會保持最後的值,而且是全局的。把循環變量全部放進局部結構(Module,Block,With)里是個好習慣。

另一方面,Do循環本身就是局部結構,它自動將循環變量局部化。然而,和我們想的不一樣的是,這種局部化是在時間上而非空間上進行的(和Block一樣),後面會詳細介紹。一般的:

In[]:= a:=i^2;
Do[Print[a], {i, 1, 5}]
Out[]= 1
       4
       9
       16
       25

i在循環結束後失去了值:

In[]:= i
Out[]= i

運算符的分塊 CompoundExpression

小括號實則是一個組合運算符,在代碼的任何地方都適用。它的完全形式是CompoundExpression[oper1;...;opern].各個操作直接用分號隔開。組合運算符的值是最後一個表達式的值(即opern)。特別地,如果我們在最後也加上分號,那麼將沒有返回值(更精確的說,返回的是空值Null)

要注意Do並不是C的do-while循環,而是For和While的快速版本,這個版本裏沒有邊界條件需要檢查,循環的次數是確定的。

局部跳轉語句:Break,Continue,Return

Break[]和Continue[]可以用來實現局部的語句跳轉。它們的工作方式和C裏面差不多。比如我們讓Do循環4次後停止:

In[]:= i = 1;
       Do[(Print["*"];If[i ++ > 3, Break[]]), {50}]
Out[]= *
       *
       *
       *

Return[]語句也可以用於從循環中退出,但是它的行為和Breakp[]不太一樣。Mathematica里有三種局部化變量結構——Module,Block和With。如果這些結構中存在For或While循環,使用Break[]只能退出循環而且循環結構後面的代碼會緊接着被計算。如果用Return的話情況就不一樣了,Return能跳出整個局部結構(如果存在多重局部結構的話,跳出第一層)。但是對於Do循環不成立,Do本身就是一個局部結構,使用Return只能跳出Do而不包括外面的局部化結構。在熟悉Module,Block,和With之後你會更清楚。

循環通常很慢!

Mathematica里的循環效率通常都不是太高。這不因為循環本身非常慢,而是因為在其它語言裏很高效的那種類似於For的循環模式反而在Mathematica里很慢。

我們用簡單的例子示範一下。我們將求籤100000個自然數之和。我們首先用循環,然後用函數式編程。

(*循环版*)
In[]:= Timing[sm =0;
                 Do[sm = sm + i,
             {1, 100000}];sm]
Out[]= {0.581, 5000050000}[6]
(*函数式版*)
In[]:= Timing[Apply[Plus, Range[100000]]]
Out[]= {0.16, 5000050000}

很明顯函數式風格要快上好幾倍!關於具體的原因會在下一章詳細介紹。Timing給出的計算所用的總時間。

看來我讓循環臭名遠揚了。為了給它留點面子,在這裏我要說,當能夠使用內置的w:編譯配合使用的時候,循環就快得不得了:

In[]:= Compile[{x,{n,_Integer}},
          Module[{sm=x},Do[sm=sm+i,
            {i, n}];sm]][0,100000]//Timing
Out[]= {0.03, 5.00005*10^9}

編譯函數的只能達到機器精度,而且編譯器不能編譯通用對象。

Mathematica中的4種括號

[編輯]

Mathematica使用4種括號,小括號,單方括號,雙方括號,和花括號。每一種括號都有自己的含義和目的。

小括號

[編輯]

小括號用來確定計算的優先級,用法和大部分語言一樣。

花括號

[編輯]

花括號表示一個(List)。完全形式是List:

In[]:= Clear[a, b, c];
       {a, b, c}===List[a, b, c]
Out[]= True

List表示對象(類型可以不一樣)的集合,對於在程序中建立塊狀結構非常重要,因為任何複雜的數據結構都可以用其建立。下一章會完全奉獻給List。

有些內置函數的參數也會用到List,比如Table,Do,以及前面說到的Level。

單方括號

[編輯]

單方括號用來構建普通表達式。它們被用來表示任意的樹狀Mathematica表達式。這是它在Mathematica中存在的唯一目的(很重要)。

你可能會說方括號還能用來做函數調用呢:

In[]:= Sin[Pi]
Out[]= 0

然後,函數在Mathematica中是坐在第二把交椅上的,老大是規則和模式。所以,所有的函數調用到最後只是普通表達式的特例。Mathematica中沒有獨立的函數調用概念,它們都是用表達式和規則實現的。

雙方括號

[編輯]

最後雙方括號被作為Part的簡寫,Part在前面介紹過,用途是從任意表達式里提取元素。一個普遍的錯誤是誤將"[]"當作"[[]]"。一定要注意這兩種方括號的區別。

總結

[編輯]

我們了解了最基本的操作,賦值、比較、控制結構、循環。這裏我們對於有些內容介紹得很簡短,因為它們在正式的編程中很少用到。

本章還舉了幾個例子來說明Mathematica以規則為基礎(rule-bases)的本質。這對於嗅到Mathematica和其它程式語言的不同之處很有幫助。

我們還學了一些函數:Clear、Set、SetDelayed、Equal、SameQ、If、Do、For、While、Apply、Range、Timing。

註記

[編輯]
  1. 這裏的Unevaluated的作用是防止裏面的內容被計算,詳見幫助
  2. 這個名詞的翻譯讓我頭疼,姑且先這麼譯了
  3. 可以藉助通配符來一次清除多個變量,比如Clear["Golbal`*"]
  4. 為了防止一個頁面的篇幅過長(好吧是我太懶了),一些不太重要的東西從略介紹,讀者可以查閱幫助
  5. 這種現象不是Mathematica中特有的,在C語言中也會發生類似的現象把inc換成++試試?
  6. 時間測定可能在不同配置的計算機及版本之間有區別