7、程序包 返回目錄頁
一、運行目錄與規則庫目錄
二、目錄和符號
三、搜索順序
四、載入程序包
五、自制程序包
六、標準程序包
表達式的計算過程,是個遞歸的過程:
一邊在規則庫中搜索規則(rule),一邊進行變換(transformation),直到無規則可用爲止。
-------------------------------------------------------------------------
一、運行目錄與規則庫目錄
當咱們打開或新建一個筆記本文件,輸入代碼,而後運行。表面上看起來很簡單。
可是呢,背後隱藏着太多的東西。
前面第四章,咱們講了表達式的運算過程,而後總結出,MMA的核心編程思想是:規則編程。
規則編程這套系統具體是怎麼構建的呢?規則庫在什麼地方呢?
若是先瀏覽一下這一章,再去看第四章,可能更容易明白。
----------------------------------------
運行目錄。
在同一個筆記本文件中,咱們能夠在不一樣的運行目錄下編程。
Begin["A`"]
$Context
End[]
$Context
得:Global`
Begin["A`"] 新建目錄A,並進入了目錄A
$Context 是個全局變量,返回MMA進程中的當前運行目錄名
End[] 退出目錄A,返回進入目錄A以前那個目錄
` 是倒引號或重音符字符(ASCII 碼是 96),在這裏是運行目錄標誌。
Global` 是全局運行目錄,一開始打開或新建筆記本文件時,這個運行目錄就自動打開。
A`x="textA"
B`x="textB"
Global`x="textGlobal"
x
三個變量x,分別在不一樣目錄下,它們的值都不一樣。
若是用全名,能夠像用通常其它符號同樣使用它們。
若是變量多時,給變量分類很方便,分別放在A、B...目錄。當變量名相同時,也不會衝突。
若是每次用全名,比較麻煩,因此咱們常常不用全名,而直接寫變量:Global`x與x,返回同一值。
----------------------------------------
規則庫目錄
當咱們輸入:Global`x="textGlobal" 時,定義了一個規則,在運行目錄下,創建了一個變量x,值是"textGlobal"。
那這個規則,保存在什麼地方呢?即規則庫在什麼地方呢?
答案是:也是保存在Global`目錄中。
因此對於Global`目錄來講,既是運行目錄,又是變換規則庫目錄。
在幫助文檔中,翻譯成了上下文(context),看起來很是彆扭。咱們在這裏,稱之爲運行目錄,或者規則庫目錄,有時簡稱爲目錄,比較好理解。
-------------------------------------------------------------------------
二、目錄和符號
運行目錄與操做系統中的文件目錄,在結構上是很像的,都是樹形結構。
文件目錄下,能夠有文件目錄與文件。
同樣地,運行目錄下,能夠有運行目錄與符號(定義、變量、規則,名稱不少,指的是同一東西)。
一些文件操做的概念也很類似,無非在MMA中,以函數的形式來操做。
$Context
Context[]
都是返回當前目錄名。
Context[x]
返回一個符號的目錄。
Clear[x]
x
清除一個符號的值
Remove[x]
?x
從系統中徹底刪除一個符號
++++++++++++++++++++++++++++++++++++++++++++++++++++++
Clear["form"] 刪除名與 form 匹配的全部符號的值
Clear["context`*"] 刪除在指定目錄中全部符號的值
Remove["form"] 徹底刪除名稱與 form 匹配的符號
Remove["context`*"] 在指定的目錄中刪除全部符號
如:
xx = 3;
xx
Clear["x*"];
xx
xx = 3;
x = 2;
Clear["Global`x*"];
xx
x
xx = 3;
xx
Remove["x*"];
?xx
xx = 3;
x = 2;
Remove["Global`x*"];
?xx
?x
++++++++++++++++++++++++++++++++++++++++++++++++++++++
Names["form"] 給出與 form 匹配的符號列表
如:
xx = 3;
x = 2;
Names["x*"]
Names["System`Ab*"]
NameQ["form"] 測試是否有與 form 匹配的已存在符號
如:
NameQ["System`Ab*"]
NameQ["System`AB*"]
(* 前兩句很好用,能夠檢查符號有木有用過 *)
Contexts[] 全部目錄組成的集合
Contexts[]
Contexts["string"] 給出匹配字符串的目錄列表
Contexts["A*"]
-------------------------------------------------------------------------
三、搜索順序
運行目錄與規則目錄,都是按樹形結構安排的。這點已經清楚了。
那麼當程序運行時,要找規則來匹配。規則目錄有不少,按什麼順序來搜索規則呢?
有一個全局變量:
$ContextPath
用來決定目錄的搜索順序。
返回可搜目錄的列表。這個列表中,目錄是按必定順序排列的。
那什麼叫「必定順序」?
當文件剛創建時,有個默認的順序:
$ContextPath
得:{..., "System`", "Global`"}
目錄"System`" 在目錄 "Global`" 以前,意味着先搜索"System`"目錄,再搜索"Global`"目錄。
("System`"目錄中,存放着全部內置函數。)
那在$ContextPath中的各個目錄中,仍是搜索不到,咋辦?
那就搜索當前的工做目錄($Context)。當前的工做目錄,就是當前的規則庫目錄。
那仍是木有,咋辦?
那就好辦了,工做完畢,表達式計算完畢,原樣輸出。
規則總的搜索順序就是:MMA老是先搜索$ContextPath列表中的規則庫目錄,再搜索當前的工做目錄。
----------------------------------------
關於搜索順序的討論。
以上總的搜索順序是說明文檔中說的。而在一本很是好的入門書的兩個版本中,說法卻均與之相反。
(
《數學軟件Mathematica入門》初版的中文版。
An Introduction To Programming With Mathematica 3Ed
)
到底誰對誰錯?
----------------------------------------
最好的辦法是實驗驗證。在驗證以前,要說個事。
$ContextPath做爲全局變量,是能夠任意修改的。這意味着,MMA徹底容許用戶自定義搜索順序。
(****************
$ContextPath = {}
1+2
*****************)
這是個破壞性實驗,全部規則庫報廢,1+2是算不出來了。
並且沒法恢復,由於這個筆記本文件已經喪失一切計算功能(無任何規則能夠匹配)。
那再新建一個筆記本文件好了?仍是不行。
只能重啓MMA了。
(嚴重警告,在作這個實驗前,把前面工做保存好!)
還好,重啓MMA正常了。下面開始作正常試驗:
A`x = "textA";
B`x = "textB";
Global`x = "textGlobal";
$ContextPath = {"B`", "A`", "System`", "Global`"};
x
Begin["A`"];
x
End[];
$ContextPath = {"A`", "B`", "System`", "Global`"};
x
Begin["A`"];
$ContextPath = {"Global`", "A`", "B`", "System`"};
x
$ContextPath = {"Global`", "System`"};
y = "textAy";
y
End[];
y
Context[y]
觀察結果,得出結論:
「MMA老是先搜索$ContextPath列表中的規則庫目錄,再搜索當前的工做目錄。」
這句話是徹底正確的,但不完整。完整的表述是:
MMA老是先搜索$ContextPath列表中的規則庫目錄,再搜索當前的工做目錄——若是還沒找到,則搜索剩下的沒有搜索過的工做目錄。
根據以上的搜索過程,得出計算過程:...若是找到,則立刻進行替換,若是找不到,則原樣輸出。
這只是徹底匹配的狀況。若是考慮到模式匹配,要在全部可能的匹配中,找到最匹配的。。
MMA是個複雜的系統。
-------------------------------------------------------------------------
四、載入程序包
在使用MMA編程中,不少時候,內置函數(共有3000個左右)已經夠用。
但在更多時候,當咱們有更多的需求的時候,但願有更多的函數可用。
每一個MMA標準版,都包含了標準程序包。
程序包(package)是函數的集合,即把函數們打包了。
要使用程序包,必需要先載入。載入以後,在程序包中函數的使用上,與內置函數已經沒有區別。
若是標準程序包還不能知足用戶的需求,用戶能夠自定義程序包。
自定義程序包在使用時也同樣要先載入。而後在使用上與內置函數也沒有啥區別。
全部程序包之外部文件的形式在操做系統的文件管理系統中存放,通常文件名爲這種形式:package.m
----------------------------------------
$Packages
給出當前MMA進程中所加載的全部程序包對應的規則庫列表。(包含Global`目錄)
通常來講,程序包對應的外部文件的文件名,與程序包內定義的規則庫目錄名稱,保持統一。
$ContextPath
返回可搜規則目錄的列表。前面已經使用過。
$ContextPath可用戶自定義,按須要改變內容。但$Packages
是受到保護的,不能夠改變。
通常程序包加載成功,系統不會有任何提示。但加載不成功,則會有錯誤提示。
加載成功意味着:
A、程序包對應規則目錄,已經添加到$Packages列表最前面。
B、程序包對應規則目錄,已經添加到$ContextPath列表最前面。
Needs["ComputerArithmetic`"]
$Packages
$ContextPath
ComputerArithmetic`是標準程序包之一,外部文件(ComputerArithmetic.m)存放目錄:
...Mathematica9\AddOns\Packages\ComputerArithmetic\
加載成功後,能夠用Names函數,來查看加載的程序包中有哪些函數可用:
Names["ComputerArithmetic`*"]
而後能夠查詢函數的功能。
?Arithmetic
而後,還能夠打開幫助文檔。(點擊右下角的 >> )
----------------------------------------html
加載程序包
Needs["context`"] 調用 Get["context`"]。
因此咱們只要學習Get函數。
<< 是Get函數的語法糖。
$Path
給出在試圖找到一個外部文件時搜索的缺省文件目錄列表。
運行一下,看看輸出。
$Path表是能夠修改的。但要注意,若是給它改爲了空表,則再也沒法載入外部文件了。
Get函數,能夠加載不少類型的外部文件。無一例外地,老是搜索每個$Path表中的每一個元素。
咱們在這裏,只關心Get["context`"]這種類型的狀況,即加載程序包文件context.m,獲得context`規則庫目錄。
具體說,Get["context`"]分兩種狀況:
A、加載文件
(潛規則是,想加載程序包con.m文件、獲得con`規則庫目錄,把二者取同名:這裏名字都是con。)
Get["con`"]
運行時,在全部$Path表中的每一個元素(即文件路徑)下,尋找con.m文件。一旦找到,就中止尋找,而後載入con.m文件。
有時候,文件都不在這些搜索路徑之下,能夠這樣寫:
Get["dir`con`"]
那就是在搜索路徑之下,尋找dir文件夾下的con.m文件。找到就中止尋找,而後載入。
還有一種寫法,與$Path表中路徑無關,直接指定若干尋找目錄,格式是:
Get[name,Path->{dir1,dir2,...}]
好比寫成這樣:
Get["SayHi`", Path -> "X:/aaa"]
即SayHi.m在X:/aaa/目錄下,能夠這樣直接找到並載入。編程
要注意的一個事是,目錄做爲一個字符串的形式表達
"X:/aaa/"
"X:\\aaa\\"
這兩種形式均可以,但不能寫成:
"X:\aaa\"
由於相似於C語言中的字符串中,\已經用於轉義,請觀察:
"X:\naa\"
得:
"X:
aa"
\n做爲一個總體,轉換成了換行符。瀏覽器
B、加載目錄
在$Path各路徑下,搜索文件與搜索目錄是同時進行的。
當文件在$Path各路徑下找不到時,找context目錄。
(這裏又有個潛規則,想加載目錄con,獲得con`規則庫目錄,則把二者取同名:con)
加載目錄的過程是,當找到con目錄時,在con目錄下尋找Kernel目錄中的init.m文件。而後運行init.m文件中的語句。(加載目錄只是用於表達一種加載方法。目錄沒法加載,真實加載的仍是文件,經過init.m中的語句。)
哪怕init.m中一句語句也沒有,MMA也會認爲工做完畢,而中止尋找。
若是找不到init.文件,則在con目錄下找匹配的程序包文件來試圖載入。
標準程序包,都是以這種「加載目錄」的形式載入的,都經過init.m文件。
因此標準程序包對應的外部文件,都這樣組織:
con目錄下,有若干程序包文件,而後還有一個文件夾Kernel,裏面有個init.m文件。
能夠用FindFile函數,找到init.m文件的具體位置:
Needs["ComputerArithmetic`"]
FindFile["ComputerArithmetic`"]ide
----------------------------------------
DeclarePackage
DeclarePackage["context`",{"name1","name2", ...}]
聲明若是任何具備指定名稱的符號被使用的話,則 Needs["context`"] 應該自動執行
$ContextPath
DeclarePackage["StatisticalPlots`", "ParetoPlot"]
ParetoPlot[{a, b, c, d, d, d, e, d, e, e, f, a, b, c}]
$ContextPath
這也是載入程序包的一種方法。
----------------------------------------
自動加載程序包
假如一個SayHi.m,存放在X:\aaa\目錄中時(操做系統爲Win系列),想要MMA每次啓動時,都自動加載,咋辦?
$BaseDirectory
$UserBaseDirectory
這兩個文件夾中(如下操做,在這兩個文件夾中的任一個,均可以),都有autoload文件夾與Kernel文件夾。
Kernel文件夾中的init.m文件可以在mma啓動的時候自動加載。
用記事本打開init.m文件,若是沒有,則建立一個init.m文件。
添加下面內容:
Get["SayHi`", Path -> "X:/aaa"]
啓動MMA:
$Packages
$ContextPath
能夠看到,SayHi`規則目錄都在裏面了。
還有一種方法,是在autoload文件夾中作文章。
在autoload文件夾下,創建一個子文件夾:SayHi
而後再在SayHi文件夾下,創建一個子文件夾:Kernel。
而後再在Kernel文件夾下,創建一個init.m文件。(只要先用記事本創建一個txt文件,而後更名就行)
init.m文件中,只有一句話:
Get["SayHi`SayHi`"]
最後,把SayHi.m文件,從X:/aaa文件夾,copy到SayHi文件夾下。
完工。
啓動MMA:
$Packages
$ContextPath
能夠看到,SayHi`規則目錄都在裏面了。
?SayHi`*
能夠看到裏面的函數了。
-------------------------------------------------------------------------
五、自制程序包
程序包能夠自制。
一方面,當程序比較大型的時候,自制程序包可使代碼容易維護,也更容易編寫。
另外一方面,經過學習程序包的自制過程,能夠更容易理解標準程序包。
----------------------------------------
下面咱們來製做自定義程序包:Say.m
選擇菜單:文件/新建/程序包。
而後在彈出窗口中開始編輯文本。
輸入如下內容:
$ContextPath
BeginPackage["Say`"]
$ContextPath
Begin["`Private`"]
$ContextPath
End[ ]
$ContextPath
EndPackage[ ]
$ContextPath
而後保存程序包,假設保存爲:X:/aaa/Say.m
而後點擊右上角的「運行程序包」,看下面的輸出結果。
這個程序,主要是爲了觀察$ContextPath的內容變化。
BeginPackage["Say`"] (* {"Say`", "System`"} *)
EndPackage[] (* 恢復到以前內容 *)
這兩句,改變了$ContextPath的內容。
而Begin["`Private`"]不會改變$ContextPath的內容。
結合第3節內容,咱們立刻了解了程序包的工做方式。
----------------------------------------
再把內容修改成:
(* ^ *)
BeginPackage["Say`"]
Say::usage = "這是Say程序包。"
Begin["`Private`"]
Say := Print["SayOut"]
SayA:= Print["SayAOut"]
End[ ]
SayB:=Print["SayBOut"]
EndPackage[ ]
點擊保存。而後再點擊「運行程序包」
注意三個地方的顏色。Say/SayA/SayB
注意把下圖所指的地方,設置成鮮豔的顏色。函數
----------------------------------------
在筆記本文件中,輸入:
Get["Say`", Path -> "X:/aaa"]
$Packages
$ContextPath
能夠看到,Say程序包載入進來了。
?Say`*
能夠看到,Say::usage部分的提示信息出來了。
Names["Say`*"]
得:{"Say", "SayB"}
Say
SayA
SayB
能夠看到,僅SayA沒有被替換。
由於SayA的定義,躲在
Begin["`Private`"]
SayA:= Print["SayAOut"]
End[ ]
之中,是外部不可見的。
因此一些臨時變量啊、局部變量啊,能夠躲到這裏面去。
沒有賦值的全局符號,能夠用特殊顏色顯示,這個很是好用,如圖:
----------------------------------------
前面不斷用到的SayHi.m,就更簡單了:
X:/aaa/SayHi.m
(* 22:35 2016-11-2 *)
BeginPackage["SayHi`"]
SayHi::usage = "這但是個人第一個程序包啊!"
SayHi := Print["必定載入成功!"]
EndPackage[ ]
筆記本中:
Get["SayHi`", Path -> "X:/aaa"]
$Packages
$ContextPath
?SayHi`*
Names["SayHi`*"]
SayHi
一邊打開程序包文件,進行改寫。
一邊在筆記本文件中進行載入輸出等調試。
這是能夠的,不用不斷重啓MMA。
只是要注意多按「保存」、「更新」。
最後強調一句:多觀察字符的顏色很重要。
-------------------------------------------------------------------------
六、標準程序包
幫助文檔中有標準程序包的詳細說明:
guide/StandardExtraPackages
Mathematica 9.0 標準程序包
只是沒有翻譯成中文。
每一個程序包,均可以去找到源代碼,在如下目錄中:
...Mathematica9\AddOns\Packages
內容很是豐富。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++學習
MMA的基本入門內容介紹完畢,接下來作什麼呢?測試
要麼去看幫助文檔:函數瀏覽器。MMA的內置函數,有三千個左右,咱們這裏只介紹了一小部分。去看按功能分類的函數瀏覽器,能夠擴大咱們的函數量(如同窗英語時擴大詞彙量)。ui
要麼去看目錄頁中介紹的相似的書,去看MMA的應用代碼。在應用中擴大函數量(用F1很方便啊)。spa
+++++++++++++++++++++++++++++++操作系統
擴展閱讀:木有。有時間的話,去瀏覽一下標準程序包的代碼