prolog教程第9章.ppt_第1頁
prolog教程第9章.ppt_第2頁
prolog教程第9章.ppt_第3頁
prolog教程第9章.ppt_第4頁
prolog教程第9章.ppt_第5頁
已閱讀5頁,還剩73頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

1、第9章 編碼風格,本章介紹Visual Prolog 6的編碼風格,包括基本元素、推薦格式、程序結(jié)構(gòu)、程序設計語用學、存儲管理,以及異常處理。這里描述的Visual Prolog程序的編碼標準,是Visual Prolog系統(tǒng)本身的一部分。且用戶文檔中的例子也是標準的,它門同樣也代表了Prolog發(fā)展中心為用戶推薦的編碼標準。,第9章 編碼風格,9.1 基本元素 9.2 推薦格式 9.3 程序結(jié)構(gòu) 9.4 程序設計語用學 9.5 存儲管理 9.6 異常處理 本章小結(jié)與習題,9.1 基本元素,9.1.1 關(guān)鍵字 9.1.2 半關(guān)鍵字 9.1.3 文字 9.1.4 標識符 9.1.5 常量 9.1

2、.6 變量 9.1.7 謂詞 9.1.8 論域 9.1.9 類和接口,9.1.1 關(guān)鍵字,關(guān)鍵字以小寫字母表示。在有關(guān)資料中,關(guān)鍵字是以沒有襯線的粗體字被編排的,例如 Arial,缺省顏色為暗黃色。例如 constants domains facts predicates class interface,9.1.2 半關(guān)鍵字,Visual Prolog使用了大量的標識以滿足多樣化的句法結(jié)構(gòu),這些詞以小寫字母書寫(除了C調(diào)用約定寫成C),且一般是沒有襯線的字體。這些半關(guān)鍵字依照它們的性質(zhì)以兩種不同的顏色顯示。如果這個詞表示一種選擇,那么它顯示為藏青色,而如果它是一種結(jié)構(gòu)詞,那么它將以暗黃色顯示。

3、,erroneous failure procedure determ nondeterm multi,stdcall C . language as .,這個例子顯示了顏色和字體。 predicates myPredicate : (string Value) procedure (i) language stdcall as _myP,9.1.3 文字,文字顯示為藍色。例如 Hello world!,9.1.4 標識符,標識符的一般格式可以由下面的EBNF語法來描述: = = _ * = + 前綴和后綴被用來表示某種標識符,并將用來處理各種標識符之間的聯(lián)系。這些詞以大寫字母書寫,當然除了整

4、個標識符的第一個字母必須小寫以外。 所有變量以大寫字母開始,而其他所有的標識符以小寫字母開始。 在文件中,除了關(guān)鍵字,所有的標識符以襯線字體編排。例如Times New Roman字體。,9.1.5 常量,常量既沒前綴也沒后綴,它以小寫字母開始。例如 numberOfRows,pi,logErrorMsg,9.1.6 變量,變量也沒前綴和后綴。像前面提到的Prolog要求的那樣,變量以大寫字母開始。在程序文件中變量以綠色顯示。,9.1.7 謂詞,謂詞沒有前綴。然而,“try”可以用來表示一個謂詞是確定性的,特別是它被用做從一個相應的程序描述中區(qū)分確定性謂詞的描述。而后者將引起一個異常而不是失敗

5、。例如: trySetValue : (integer Value) determ (i). setValue : (integer Value) procedure (i). 除非為了避免混淆必須添加后綴,否則謂詞是沒有后綴的。在一些情況下,為避免混淆,表9-1中的后綴應該被選用。 注意:一般來說,多重謂詞應以_nd為后綴,但如果環(huán)境需要也可用_multi代之。,9.1.7 謂詞,表9-1 常用的謂詞后綴,9.1.8 論域,論域沒有前綴,_list被用做列表論域的后綴。在多數(shù)情況下,列表論域沒有域名。例如,一個數(shù)據(jù)庫記錄是一個值的列表,但是記錄是列表值首選的一個更好的論域名。注意論域以小寫字

6、母開始。這同樣適用于論域如字符串,整數(shù)等。例如 string value record record_list,9.1.9 類和接口,類和接口沒有前綴。例如 String inputFile template inputStream 傳統(tǒng)的COM 接口以“I”開始,現(xiàn)在這個“I”被保留了下來, 但變成了小寫: iUnknown iDispatch,9.2 推薦格式,這一節(jié)考慮程序代碼的格式。通過格式化,我們可以表示折行(line breaking),縮排(indentation)和對齊(alignment)??s排指行開始處的空格的數(shù)量,而對齊指非行首字符的排列結(jié)構(gòu)。,9.2 推薦格式,9.2.

7、1 折行 9.2.2 縮排 9.2.3 對齊 9.2.4 空格字符,9.2.1 折行,折行遵守如下規(guī)則: 一行通常不應超過70個字符。 外部句法結(jié)構(gòu)總是在內(nèi)部結(jié)構(gòu)之前被斷開。 不同謂詞的子句至少用一個空行分開。 同一謂詞的子句不應被一個空行分開。 一個段的關(guān)鍵字之前至少有一個空行。 截斷(無論它看起來如何微?。┍旧響搯为氄家恍?。 一個子句的頭在一行。,9.2.2 縮排,通過縮排,實現(xiàn)在行首的空格數(shù)量??s排遵守如下規(guī)則: 縮排可由相同的步驟實現(xiàn)(例如4個空格)。 如果一套括號的部件必須被分在幾行中書寫,那么在開括號后面必須立即插入一個折行,且對于開括號縮排增加一步(沒有對齊)。,9.2.3 對

8、齊,對齊指的是排列結(jié)構(gòu),這種結(jié)構(gòu)要么不是一行的開始,要么是通過縮排規(guī)則被對齊成行的一行的開始。 對齊沒有被使用。,9.2.4 空格字符,逗號后面的空格可以被省略。 在算符或列表里面,逗號后面的空格可以被省略。 在聲明謂詞、事實、常量中,“:”之前或之后的空格。 括號前后沒有空格,除非這個括號與一個被空格所包圍著的記號相鄰,例如 :- , : 。,9.3 程序結(jié)構(gòu),9.3.1 段 9.3.2 類、接口及實現(xiàn) 9.3.3 謂詞聲明 9.3.4 論域 9.3.5 子句 9.3.6 不確定性循環(huán) 9.3.7 Word格式化代碼,9.3.1 段,段關(guān)鍵字本身就在一行中。如果段關(guān)鍵字被縮排n步,那么結(jié)構(gòu)就

9、被縮排n+1步。段與段之間必須至少有一個空行將其分開。 clauses p. clauses q.,9.3.2 類、接口及實現(xiàn),開類和閉類本身在一行。閉類包含類標識符,類里面的段關(guān)鍵字比類本身多縮排一步。 class specialOutputFile : outputFile predicates . end class specialOutputFile 這同樣適合其它類型(如,接口等)。,9.3.3 謂詞聲明,謂詞聲明總是將一些名字作為變元,這些名稱被格式化為變量。模式的聲明是可選擇的。 Predicates increment : (integer X) - integer Y. bu

10、bbleSort : (integer_list Input) - integer_list SortedList. myPredicate_nd : (aVeryLongDomainName StrangeFirstParamanter, anotherLongDomainName PlainSecondParameter) nondeterm (i,o) dterm (i,i). 注意,在開括號和縮排正常加大之后的折行?;蛘咚械淖冊谝粋€單行或者每一個變元分開在不同的行。,9.3.4 論域,算符的參數(shù)總是有名稱的,這些名稱被格式化為變元。如果論域聲明被斷開成幾行,那么在等號標記后的是第一

11、個斷行?;蛩械乃惴谝粋€單行,或每一個算符單獨在一行?;蛞粋€算子的所有參數(shù)占據(jù)一個單行,或每一個參數(shù)占據(jù)一個單行。,Domains value_list = value*. value = int(integer Value); real(real Value); str(string Value). aVeryLongDomainName = x(interger X); y(integer Y). anotherLongDomainName = aFunctorWithManyArguments( integer X, integer Y, integer Z, integer RedC

12、olourComponent, integer BlueColourComponent, integer GreenColourComponent).,9.3.5 子句,子句頭本身在一行里。子句體中每個調(diào)用在一行中。如果子句頭必須被斷開,那么變元比子句頭多縮排兩步,否則變元和子句體將被縮排在同一位置。,clauses myPredicate(X, Y) :- callNoOne(X, Y, Z), !, callNoTwo(Z, Y). myPredicate(X, Y) :- callNoThree(X, Y). aPredicateWithManyArguments(FirstArgume

13、nt, SecondArgument, X, Y, Z, ErrorNo, ErrorMsg) :- callNoOne(FirstArgument, SecondArgument, X, Y, Z, ErrorNo, ErrorMsg), callNoTwo(), ,9.3.6 不確定性循環(huán),在prolog中常用的一種結(jié)構(gòu)是在不確定性調(diào)用結(jié)果之上的循環(huán)。對這種結(jié)構(gòu)我們打算將循環(huán)體縮排為一個額外的層次。 clauses. myPredicate() :- member(X, 1,2,3,4,5,6) doAction(X), % extra indentation in the loop bo

14、dy fail. myPredicate().,9.3.7 Word格式化代碼,這里將給我們一些提示,使微軟文字處理軟件Word的格式化代碼變的更容易。 我們已經(jīng)構(gòu)造了一個代碼段風格,此外還有許多風格,如:關(guān)鍵字(keyword),參數(shù)(parameter),變量(var),文字(literal)等各種風格。 這個段落代碼風格有下面的性質(zhì): 1)它遵照了文本段落體的格式。這種格式在代碼段下提供了合適的空格。 2)它避開了驗證(例如:拼寫檢查)。 3)它縮排一定數(shù)量,并將默認的TAB鍵大小設置為同樣的數(shù)量。我們用0.63cm,因為它是其它段落格式中的默認尺寸。 它保持相同特性的行在一起,以便程序

15、部件不會跨頁。,返回,9.3.7 Word格式化代碼,關(guān)鍵字的字符風格將字體變成Arial,將字體版面樣式變成粗體,將顏色變成暗黃。變量風格將變量顏色變成綠色,文字風格將文字顏色變成藍色。 在打印代碼的時候,應首先選擇代碼段風格,并使用軟折行(shift+newline)鍵入代碼,使用TAB進行縮排(在tools - options - edit頁面上重置insert and backspace set left indent選項)。 Timesaver(節(jié)省時間):雙擊關(guān)鍵字之一并選擇關(guān)鍵字字符的風格。關(guān)鍵字一直選中的同時雙擊格式繪圖筆(工具欄中的畫筆),接著單擊所有的關(guān)鍵字一次,當所有的關(guān)

16、鍵字被著色后,按下ESC鍵或再次點擊格式繪圖筆。,9.4 程序設計語用學,當用Visual Prolog 6編寫程序時,Visual Prolog最好的實踐包括Prolog發(fā)展中心(PDC)使用的格式建議與忠告。 該描述打算被用在新的Visual Prolog代碼中,而PDC不希望將已有的代碼更新為下面這些標準。因此,在我們看到的Visual Prolog發(fā)行版本中可能沒有下面這些標準。 注意,這些指南包含負面例子,例如:有毛病或有錯誤風格的例子等。這些例子(或者負面部分)將以紅色顯示,有時甚至以粗體顯示。,9.4 程序設計語用學,9.4.1 常規(guī)技巧 9.4.2 布爾值 9.4.3 截斷 9

17、.4.4 紅色截斷和綠色截斷 9.4.5 指派輸入格式 9.4.6 異常和錯誤處理 9.4.7 內(nèi)部錯誤和其它錯誤,9.4.1 常規(guī)技巧,一個謂詞的子句通常應該少于20行,否則,我們應該考慮引入輔助謂詞來處理次要任務。有時一個謂詞可能占據(jù)多于20行。例如一事物有50個屬性必須被設置,且每個設置需要一個調(diào)用就是一個例子。 使用完全合格的名稱(例如:someClass:method),不管什么時候它都使程序變得更清晰。 當與類名一起讀時,類方法應是一個有意義的名稱。在方法名中避免重復類名,例如,這種名稱可以是: someClass:method 而不是 someClass:someClassMet

18、hod 一個謂詞應該只做一件事。因此,如果一個謂詞用來處理列表中的每一個元素,那么考慮將它分成兩個謂詞,一個瀏覽這個列表,一個用來處理這些元素。這樣做的優(yōu)點是使謂詞變得更簡單,因此更容易糾正,更容易理解。,9.4.2 布爾值,如果某事件或真或假,那么應該只用一個布爾變量。程序員不應該用它去區(qū)分兩個不同的事件,例如左與右,水平與垂直。在這些情況下,程序員應該用具體的論域來代替。 domains direction = left(); right() orientation = horizontal(); vertical() 當一個邏輯變量以真條件命名,此時一個變元表達了一個這樣的事實:如果事件

19、為真就公布,若為假就不公布,我們稱這樣的變元為可公布的。,9.4.3 截斷,截斷是一種謂詞,它去掉了不確定性,例如:去掉了更進一步求解的可能性(名稱由此得來)。 它截斷的這種不確定性可分為兩組,雖然有些截斷落在兩組中: 1)截斷是阻斷回溯的可能性而轉(zhuǎn)到當前謂詞中緊接著的下一個子句。 2)截斷是阻斷對一個不確定性謂詞調(diào)用的更進一步的解決方法。 除了上面所述的兩種用法之外,截斷再沒有其他合理的用法。一旦理解了這些目的,將截斷放在一個正確的位置將是一件很容易的事。 1)或者將截斷放在不再需要回溯后續(xù)子句的位置。 2)或者將它放在一個不確定性謂詞的調(diào)用之后。對此,有一個惟一的解決方案是非常重要的。,9

20、.4.3 截斷,第一個目的可以由下面的例子解釋: clauses p(17, X) :- X 13, !, q(X), ., p(A, X) :- . 在這個例子中,程序中測試X 13之后有一個截斷。這是第一個合理使用截斷的一個非常典型的理由:“子句實例對輸入進行測試且在直接測試X13之后,即可找到正確的答案”。 一般來說,這樣的一個截斷典型情況下是放在子句頭的后面,要么放在緊挨子句頭的一個測試后面。,9.4.3 截斷,第二個目的可以由下面的例子解釋: clauses firstMember(X, L) :- member(X, L), !. 在這個例子中,截斷被“立即”放在一個不確定性謂詞的

21、后面,我們只對一個解決方案有興趣。 上面我們兩次突出了單詞“立即”,這是因為在放置截斷時這個關(guān)鍵字是“立即”的,即截斷應盡可能早地被放置在子句中。 對于包含多于一個截斷的子句,我們應抱懷疑態(tài)度,一個多于一個截斷的子句常常暗示了一個語法錯誤或一個設計錯誤。,9.4.4 紅色截斷和綠色截斷,通常,我們不鼓勵將截斷表示為綠色,紅色截斷相當完美。 傳統(tǒng)的Prolog體系中已定義了紅色截斷和綠色截斷的概念。簡單地說,一個綠色截斷出現(xiàn)時改變了謂詞的語義,而紅色截斷則沒有。 顯然,所有截斷一個不確定性謂詞的更進一步的解決方法的截斷自然是紅色的。因此,區(qū)分紅色截斷和綠色截斷只有一個目的,即阻斷回溯到下一個子句

22、。,9.4.4 紅色截斷和綠色截斷,考慮下面子句: clauses p(X) :- X 0, !, . p(X) :- X = 0, . 上面謂詞中的截斷是綠色的,因為如果我們移動這個截斷,這段謂詞仍以同樣的方式運行。如果這個截斷出現(xiàn)在第一個子句中,那么第二個子句的測試X =0其實是不需要的:,9.4.4 紅色截斷和綠色截斷,clauses p(X) :- X 0, !, . p(X) :- . 然而如果沒有這個測試,這個截斷將變成紅色。因為現(xiàn)在如果我們移動這個截斷,謂詞將以不同的方式運行。 綠色截斷可能似乎是多余的,但是,事實上它們被用做刪除多余的回溯點(主要是考慮性能的原因)。在Visua

23、l Prolog中,綠色截斷可能也被用來使編譯器信服某些謂詞可能有特殊的模,例如:procedure。,9.4.5 指派輸入格式,用一個調(diào)度程序僅能處理簡單的事情。用調(diào)度程序處理堵塞問題,這一問題有一個非常單調(diào)的結(jié)構(gòu),特別是有許多不同的事件一定發(fā)生。因此,邏輯上相關(guān)聯(lián)的代碼擴展了,且基本上沒事可作的代碼塊彼此緊挨著。 為了避免邏輯上相關(guān)聯(lián)的代碼被放在歸類為一種模式的操作謂詞中,所以調(diào)度程序僅調(diào)用處理謂詞。在這種方式里,調(diào)度程序僅僅是調(diào)度程序,相關(guān)聯(lián)的代碼被靠近放置。 1) 如果一個謂詞處理很多情況,那么保持每一種情況簡單化。 2) 如果一個謂詞也處理其它的案例,那么同一個案例中不要有很多的子句

24、。 第一個規(guī)則比較容易理解,第二個規(guī)則可由下面的例子加以解釋:,9.4.5 指派輸入格式,clauses qwerty(17, W, E, R, 13, Y) :- ., % A !, . % B qwerty(17, W, E, R, 13, Y) :- ., % C qwerty(Q, W, E, R, 13, Y) :- . % D . 上面的子句表示了錯誤的代碼格式,因為它對同一個輸入格式有兩個子句。如果這是謂詞處理的惟一方式,那么這樣就行了。但是在這種情況下,我們也有其它形式的子句。我們認為上面的謂詞應該被重寫,以便謂詞子句qwerty的目的是為了規(guī)定輸入的具體格式,而將其它工作留給

25、子謂詞。,9.4.5 指派輸入格式,clauses qwerty(17, W, E, R, 13, Y) :- !, % we have cased out, this is one of our cases qwerty_17_w_e_r_13_y(W, E, R, Y). qwerty(Q, W, E, R, 13, Y) :- !, % we have cased out, this is one of our cases qwerty_q_w_e_r_13_y(Q, W, E, R, Y). . clauses qwerty_17_w_e_r_13_y(W, E, R, Y) :- .

26、, % A !, . % B qwerty_17_w_e_r_13_y(W, E, R, Y) :- . % C clauses qwerty_q_w_e_r_13_y(Q, W, E, R, Y) :- . % D,9.4.5 指派輸入格式,這些代碼已經(jīng)將qwerty謂詞變成將各種輸入組合包裝起來的謂詞。像上面解釋的那樣,對于每一種輸入僅能有單個謂詞調(diào)用,這并不是本質(zhì)。主要的一點是不能以同樣的輸入方式從一個子句回溯到另一個子句。 這個規(guī)則特別被用于事件管理者,在同一事件處理中,不應當用多個子句去關(guān)閉或做其它事情。一個事件處理器經(jīng)常分布在幾頁中,因此,如果它不具有一個案例被單一子句處理這樣一個

27、規(guī)則,那么,就必須去查看所有的子句以確保知道單個輸入格式是怎樣被處理的。,9.4.6 異常和錯誤處理,當一個異常或錯誤發(fā)生時引起一個exception:raise異常。 如果想處理這個異常,就必須先捕獲這個異常。 最終使用的時候,如果我們想在異常情況下運行某些代碼,那么繼續(xù)這種異常。,9.4.7 內(nèi)部錯誤和其它錯誤,我們應該區(qū)分內(nèi)部錯誤,以及與工具、模型、類、單元等有關(guān)的錯誤。如果某一內(nèi)部不變量被破壞,那么它是內(nèi)部錯誤。典型的內(nèi)部錯誤的例子是: 數(shù)據(jù)庫中應定義的事實沒有被定義。 一個謂詞應是一段程序,但編譯器并不認可通過加一個失敗的子句構(gòu)成的謂詞程序。如果那個子句是可達到的,那么是基于了這樣一

28、個假設:即以前的子句不能失敗(一個不變量)是錯誤的。 內(nèi)部錯誤應該每單元分配一個異常,我們應總是使用一個異常:一個用作用戶錯誤,一個用作內(nèi)部錯誤(每單元用一個)。典型的用戶錯誤有: 如果一個下標超出了限制 如果一個窗口操作是錯誤的。 如果以錯誤的順序調(diào)用謂詞。,返回,9.4.7 內(nèi)部錯誤和其它錯誤,以下是我們想要捕獲出口的兩個原因: 1) 因為某人想要處理異常,比如說打開一個文件,得到一個出口,說明這個文件不存在。在這種情況下想捕獲這個出口并顯示一些錯誤信息,以說明這個文件不存在。當然如果這個異常不是某人想處理的異常中的一個,那么就必須繼續(xù)這個異常。 2) 因為我們不管謂詞是否存在而想要做一些

29、事情,一個典型的例子是:我們得到了一些做某事的鎖,并打開了這把鎖。在這里我們想要確保,如果某事退出的話,這把鎖也被打開。因此我們最終使用它。,9.5 存儲管理,本節(jié)介紹在Visual Prolog中是如何管理存儲器的。 從Vip5到Vip6,存儲器處理做了徹底的改變, 尤其是因為現(xiàn)在的堆是依靠垃圾回收箱管理的。 因而在本節(jié)中,我們將學習運行堆棧、G-堆棧、堆和垃圾回收箱。,9.5 存儲管理,9.5.1 存儲器 9.5.2 運行堆棧 9.5.3 尾部調(diào)用優(yōu)化 9.5.4 運行棧耗盡 9.5.5 全局棧 9.5.6 G-堆棧耗盡 9.5.7 堆和垃圾回收 9.5.8 垃圾回收 9.5.9 Fina

30、lizers 9.5.10 數(shù)據(jù)在什么地方分配 9.5.11 堆中數(shù)據(jù) 9.5.12 多線程和存儲,9.5.1 存儲器,Vip6程序使用三種存儲數(shù)據(jù)的方法:運行堆棧,全局堆棧,堆。 所有這些存儲器都由各自的機制維持,并有各自的用途。,9.5.2 運行堆棧,運行堆棧用于參數(shù)和局部變量。當引入謂詞調(diào)用時,其參數(shù)首先被壓到運行堆棧,然后進行謂詞調(diào)用。返回地址也傳入運行堆棧。 參數(shù)命令和返回地址的順序都是依賴于調(diào)用約定的,調(diào)用約定通過使用“l(fā)anguage”關(guān)鍵詞進行聲明。 當輸入謂詞時,它將在運行堆棧中為局部變量和一些數(shù)據(jù)分配空間。 所有收集的參數(shù)、返回地址、局部變量等等,統(tǒng)稱為堆棧幀。當謂詞執(zhí)行結(jié)

31、束時,堆棧幀將被移去。 迄今為止,這種處理與多數(shù)其他的程序設計語言中的處理一樣,但是在處理不確定性謂詞時區(qū)別較大。,9.5.2 運行堆棧,當謂詞帶激活的回溯點返回時,如果使用了回溯點,則需要堆棧幀。因此,當謂詞帶激活的回溯點返回時,堆棧幀將不再被移動。 如果回溯點出現(xiàn)在嵌套調(diào)用中,堆棧幀也將保存。因而一個回溯點可以維持一個完整的堆棧幀“鏈”存活。 如果回溯點被刪除,那么相應的堆棧幀鏈也將被移動。除非我們能確認該鏈棧頂,否則我們相信,無論何時,截斷都能刪除堆棧幀的“頂部”鏈(當然也刪除了回溯點)。 運行堆棧經(jīng)常被簡稱為棧, 有時也稱為堆(尤其是在其他的語言里沒有回溯的概念,因而當控制返回給調(diào)用者

32、時,不保留堆棧幀)。,9.5.3 尾部調(diào)用優(yōu)化,習慣上尾部調(diào)用謂詞被寫成遞歸謂詞,也就是說,謂詞訪問自身。例如,表處理。每次謂詞訪問它本身時,將創(chuàng)建一個新的堆棧幀,如果這個表特別長,那么相當數(shù)量的堆棧幀將被創(chuàng)建。 由于堆棧幀有一個固定的尺寸(一旦它被創(chuàng)建)這對表的最大長度作了個限制。 Vip6 與Visual Prolog的以前版本一樣,執(zhí)行一種最優(yōu)化,通常被稱作尾部調(diào)用優(yōu)化。其思想是:在最后訪問謂詞后,如果當前的堆棧幀不需要它,那么在調(diào)用尾部謂詞前,它將被刪除。 在這種思想的實際處理中有很多詳細的資料,但是重要的事情是尾調(diào)用比其它調(diào)用使用更多的有效棧。然而,如果在謂詞中有一個回溯點,就需要堆

33、棧幀的撤回。,9.5.4 運行棧耗盡,如果我們曾用完運行棧,最大的可能性是在遞歸謂詞中,或在遞歸謂詞的鏈上。在這里,與尾調(diào)用一樣遞歸不會發(fā)生?;蛴捎诨厮蔹c的原因,必須保留堆棧結(jié)構(gòu)。,9.5.5 全局棧,簡單的數(shù)據(jù)如數(shù)字是圍繞運行堆棧幀簡單的被復制,但是在列表被作為參數(shù)或作為返回結(jié)果傳遞時,拷貝長的列表將是非常昂貴的。因此像列表這樣的大量數(shù)據(jù)將區(qū)別處理。大量數(shù)據(jù)既可以存儲在所謂的全局棧中(常叫做G-堆棧),也可以存儲在堆中。堆將在下面進行討論,這里我們將集中討論G-堆棧。 這樣的大量數(shù)據(jù)被表示為數(shù)據(jù)本身的指針,正如上面描述的指針在堆棧幀被復制一樣,實際的數(shù)據(jù)一般存放在同一位置。 一旦列表和其它算

34、符值被創(chuàng)建,它們將被存儲在G-堆棧中,它們將常駐在那里直到G-堆棧被彈出。 在任何需要的時候G-堆棧將像堆一樣增長,當回溯時它們將被再次彈出。其基本原理是, 如果謂詞失敗,就不再需要數(shù)據(jù)了。我們這里將不進一步詳細描述這個機理。,9.5.6 G-堆棧耗盡,避免G-堆棧耗盡的惟一方法就是回溯點。這看起來也許有點笨拙,因為正常的回溯點是在不能找到問題的解時要做的事。 這可能確實是個問題:一種以此方式寫出的程序,即一個一直僅僅尋求預期路徑的程序可能是因G-堆棧耗盡而中止。 GUI程序從事件處理器中獲得一些幫助,因為這些程序總是因失敗而中止,并因此而釋放G-堆棧。每當事件處理結(jié)束,G-堆棧被重置為處理程

35、序開始時的大小。,9.5.6 G-堆棧耗盡,我們應該了解一下典型的失敗回路: clauses ppp() :- generator (X), action (X), fail. ppp(). 在執(zhí)行之前和執(zhí)行之后(由于失敗引起的回溯)的G-堆棧具有相同的大小。 與Vip5相比,Vip6在堆中存放比G-堆棧中更多的數(shù)據(jù),因而G-堆棧問題比在Vip5中要少發(fā)生。 Vip6只在G-堆棧中放置算符數(shù)據(jù),字符串和對象總是放置在堆中。,9.5.7 堆和垃圾回收,堆被用于數(shù)據(jù),這些數(shù)據(jù)必須使回溯存在。而對于那些不能被移動的數(shù)據(jù)(有關(guān)更多的內(nèi)容在下面敘述),基本上指的是事實庫和對象。但是為了澄清下面的描述,其

36、他數(shù)據(jù)也可以存儲在堆中。 堆通過垃圾回收箱來管理。程序員從堆中分配存儲器(隱含地而不是明確地),但是它將不再釋放存儲器(隱含地或明確地)。取而代之的垃圾回收箱,它不斷地分析堆并且釋放不再需要的存儲器,即它收集垃圾。 這是管理堆的原則。當需要堆存儲器的時候,可能發(fā)生下面三種情況之一: 垃圾回收箱有一個是可利用的適當?shù)亩汛鎯ζ鞫危摱伪环祷亍?從操作系統(tǒng)中分配新的內(nèi)存。 堆是回收的垃圾,而分配是重新嘗試。,9.5.8 垃圾回收,Visual Prolog 6使用傳統(tǒng)的Boehm-Demers-Weiser垃圾回收箱。 讓我們考慮一個程序,這段程序只分配存儲器,從不對其重新分配。 在某一時間程序要做

37、一些分配,但是,不是所有這些分配都能在程序中達到。程序只能存取存儲器的直接指針或間接指針。但是當程序退出子程序時,指針就無效。子程序中的變量和參數(shù)也不再存在。 程序能達到的存儲器和數(shù)據(jù)被稱作活數(shù)據(jù),其余的是死存儲器或垃圾。 垃圾回收箱的目的是到處查找垃圾并使它能再使用(為活數(shù)據(jù))。,9.5.8 垃圾回收,為了查找垃圾,垃圾回收箱負責查找和標記全部的活數(shù)據(jù),在此過程中,所有沒被標記的存儲器分配都是垃圾。 為了標記活的數(shù)據(jù),垃圾回收箱從所謂的根系集合(root set)開始,它是全局變量加上現(xiàn)有的局部變量和參數(shù)。所有從根系集合指向的存儲器被標記,否則所有從標記的存儲器指向的存儲器被標記。當不再有存

38、儲器可以用這種方式被發(fā)現(xiàn)時,所有的活數(shù)據(jù)即被標記,而所有沒被標記的存儲器是垃圾。 在垃圾回收箱回收垃圾之前,它為垃圾運行finalizers。finalizers是一個可選的用戶自定義的例程,這個例程僅在一段存儲器被收回前執(zhí)行。運行相應的finalizers之后,垃圾被回收,這樣存儲器就能再使用。,9.5.9 Finalizers,Visual Prolog 6在對象上支持finalizers:為了擁有finalizers,在一個對象構(gòu)造類中簡單地用謂詞finalizers/0編寫子句。該謂詞被隱含定義,而不能顯式定義。 注意:以“waves”形式回收存儲器是垃圾回收算法的自然特性,在一段時間

39、,有可能沒有回收什么,有可能回收了很多,所以finalizers以“waves”的形式運行。 應該記住,當存儲器處于非活動狀態(tài)時,finalizers不是(必要地)立即執(zhí)行。在垃圾回收期間,當垃圾回收箱回收存儲器時,執(zhí)行才開始發(fā)生。,9.5.10 數(shù)據(jù)在什么地方分配,上面我們提到運行堆棧保存參數(shù)和局部變量,但那只是部分正確的。實際上運行堆棧只保存數(shù)字、字符和指針,任何非數(shù)字或字符的東西被表示為實際數(shù)據(jù)的指針。實際的數(shù)據(jù)可存儲在G-堆棧或堆中。 同樣也提到,G-堆棧在回溯點上被彈出,因而任何激活回溯的數(shù)據(jù)必須存儲在堆中。當數(shù)據(jù)被聲明為事實時,它必須激活回溯,并且該數(shù)據(jù)被從G堆棧復制到堆(如果它是

40、新近被存儲在G-堆棧)。 數(shù)據(jù)從不以別的方式被復制,數(shù)據(jù)一旦在堆中,那么它將一直存在于堆中,直到它被垃圾回收站所回收。,9.5.10 數(shù)據(jù)在什么地方分配,確實沒必要擁有一個G-堆棧,在G-堆棧中被分配的空間同樣可在堆中被分配。但G-堆棧比堆有一些性能上的優(yōu)勢:分配和去分配相當快,因為它只是改變一個指針。換句話說,無論數(shù)據(jù)是否是真活著,分配在G-堆棧中的數(shù)據(jù)必須停留在那里,直到回溯時釋放它。這樣G堆棧將常常包含一些不可回收的垃圾。 因為G-堆棧既有優(yōu)點又有缺點,我們對什么樣的數(shù)據(jù)放在G-堆棧沒有約定。實際上,我們甚至不能保證G-堆棧將在未來繼續(xù)存在。,9.5.11 堆中數(shù)據(jù),對象始終存儲在堆中,

41、主要原因是對象不能被復制,亦即不能從G-堆棧到堆進行復制。 對象不僅是一個值,它還攜帶有變化的狀態(tài),如果對象被復制,將存在對象的兩個版本,一個對象的改變不會在另一個對象中被觀察到。因而從一開始,對象就被存儲在堆中。 字符串是被存儲在稱為原子堆的堆的特殊部分。堆的這部分是非常有效的。但是它只能被用于不包括任何指針的數(shù)據(jù)。之所以有效是因為它在垃圾回收期間從來不掃描指針。當我們已經(jīng)知道數(shù)據(jù)不包含指針時,就沒有必要去掃描它們。 同樣提到,存儲在事實中的數(shù)據(jù)也存儲在堆中。,9.5.12 多線程和存儲,多線程程序中的每個線程執(zhí)行獨立于其他線程的謂詞調(diào)用和回溯。因此每個線程有它自己的堆和G-堆棧。換句話說,

42、只有一個堆被所有的線程所共享。 只有一個線程能存取運行堆棧和G-堆棧,因此即使沒有更進一步的同步機制,它也能被存取。換句話說,在堆中的數(shù)據(jù)能被所有的線程存取。Visual Prolog不強迫存儲機制有任何的同步。程序員必須保證以盡可能明智的方式存取數(shù)據(jù)。 除了事實庫(包括對象中的那些)之外,Visual Prolog中的數(shù)據(jù)是不可變的。不可變數(shù)據(jù)沒有任何的同步問題,很多線程可以同時讀取那些數(shù)據(jù)。 當兩個(或更多)線程同時更新同一段數(shù)據(jù)時將出現(xiàn)問題。這樣,如果兩個或更多線程同時從同樣的事實庫聲明或撤消事實時,可能會遇到問題。,返回,9.5.12 多線程和存儲,聲明和撤消例程的執(zhí)行可以確保事實庫有

43、一個好的結(jié)構(gòu),從某種意義上說,事實數(shù)據(jù)庫的后續(xù)使用將不會引起任何的違規(guī)存取。 但是,如果兩個或更多的線程同時更新事實數(shù)據(jù)庫,一些更新可能會被丟失。舉例來說,如果兩個事實被同時聲明,這可能造成一個事實被插入數(shù)據(jù)庫中,另一個事實被丟失。 在讀取事實庫的同時更新它是合理的。 PFC提供了同步方法,可將它用于同步事實更新。,9.6 異常處理,所謂異常是指一段程序背離其正常的執(zhí)行路徑。PFC的異常包,包含了捕獲異常的謂詞,它們會出現(xiàn)在prolog程序中。 這一節(jié)敘述如下內(nèi)容: 1) 如何處理異常; 2) 如何構(gòu)造自己的異常; 3) 怎樣繼續(xù)另一個異常。 本節(jié)討論的范例程序是exception.zip,可

44、以在網(wǎng)上下載。 同時還可以參見Visual Prolog的在線幫助里“Prolog基類/異?!币还?jié)中關(guān)于異常的詳細描述。,9.6 異常處理,9.6.1 如何捕獲異常 9.6.2 如何構(gòu)造自己的異常 9.6.3 如何繼續(xù)另一個異常,9.6.1 如何捕獲異常,首先考慮這樣一段程序,程序的任務是讀取一個文本文件并把其內(nèi)容打印到控制臺。PFC提供了file:readString謂詞,它能完成上述任務。但是,有一些情況可能妨礙實現(xiàn)該任務;例如,一個指定的文件名可能指向一個根本不存在的文件。當謂詞file:readString無法完成任務時,它就產(chǎn)生一個異常。 Visual Prolog提供了一個內(nèi)置的謂

45、詞trap/3來捕獲異常并對此進行處理。關(guān)于謂詞trap/3的更詳細的內(nèi)容,可參見Visual Prolog的在線幫助中的相關(guān)主題(“LanuageReference - Built-in Domains, Predicates and Constants- Predicates -trap”主題)。,9.6.1 如何捕獲異常,捕獲一個異常的代碼為: trap(MyTxtFileContent=file:readString(myFileName,_IsUnicode), ErrorCode,handleFileReadError(ErrorCode), 最有趣的部分是謂詞handleFile

46、ReadError的實現(xiàn): 當預期的fileSystem_api:cannotcreate異常出現(xiàn)的時候,具有捕獲和處理這一異常參數(shù)的異常處理謂詞exception:tryGetDescriptor恰好正確地被調(diào)用。例子當中,我們使用控制臺的輸出來給出異常原因的解釋: stdIO:write(Cannotloadfiledueto:,ExceptionDescription, nFileName:,FileName, nReason:,Reason), 完整的例子程序可參見項目catchExceptioncatchException.prj6。,9.6.2 如何構(gòu)造自己的異常,讓我們考慮一個檢

47、查特定文件的長度或大小的程序。PFC提供了一個謂詞file:getFileProperties來返回一個特定文件的長度。如果文件長度為0,我們可以通過謂詞exception:raise來構(gòu)造一個異常。同時有必要創(chuàng)建一個謂詞,以此指定該異常。為此,我們已經(jīng)創(chuàng)建了myExceptionZeroFileSize。構(gòu)造一個異常的代碼如下:,clauses run():- console:init(), trap(file:getFileProperties(myFileName, _Attributes,Size,_Creation, _LastAccess,_LastChange), ErrorCo

48、de, handleFileGetPropertiesError(ErrorCode), Size=unsigned64(0,0),% file size is zero !, exception:raise(classInfo, myExceptionZeroFileSize, namedValue(fileSystem_api:fileName_parameter, string(myFileName). run().,9.6.2 如何構(gòu)造自己的異常,謂詞exception:raise的第一個參量是classInfo謂詞,它是由VDE為任何新創(chuàng)建的類而生成的。當然,在一個異常產(chǎn)生時,指明額

49、外的信息是一個好的方法。在我們的例子中,文件的名字非常有用,因為零長度歸屬于所指定的文件。 謂詞myExceptionZeroFileSize用以下模板生成: clauses myExceptionZeroFileSize( classInfo, predicate_Name(), Filesizecannotbezero). 這就是說,異常謂詞的第一個參數(shù)和第二個參數(shù)通常分別是classInfo和predicate_Name(),而第三個參數(shù)包含該異常原因的一個文本解釋。 完整的例子程序可參見項目raiseExceptionraiseException.prj6。,9.6.3 如何繼續(xù)另一個

50、異常,我們考慮一個DLL程序,該程序讀取一個文本文件,然后用一個參數(shù)返回其內(nèi)容。如果異常出現(xiàn)了,那么DLL繼續(xù),同時補充一個關(guān)于DLL版本的額外信息。繼續(xù)該異常的代碼如下: constants dllVersion=1.20.0.0. clauses loadFile(FileName)=FileContent:- trap(FileContent=file:readString(FileName,_IsUnicode), ErrorCode, exception:continue(ErrorCode, classInfo,continuedFromDll, namedValue(versio

51、n_Parameter,string(dllVersion).,9.6.3 如何繼續(xù)另一個異常,謂詞continuedFromDll聲明如下: predicates continuedFromDll:core:exceptionas _ContinuedFromDll12 它從該DLL中輸出。謂詞continuedFromDll的實現(xiàn)由下面的模板產(chǎn)生(與前面的myExceptionZeroFileSize相同): clauses continuedFromDll( classInfo, predicate_Name(), ExceptioncontinuedfromcontinueExcept

52、ion.DLL). 當謂詞exception:continue繼續(xù)該異常的時候,會增加一個關(guān)于DLL版本(dllVersion)的額外信息。 完整的例子可參見項目continueExceptioncontinueException.prj6。,9.6.3 如何繼續(xù)另一個異常,讓我們看看如何捕獲這樣一個繼續(xù)的異常。其代碼與上面討論的代碼類似。在這里,我們只集中考查二者不同的方面。 constants myFileName=my.txt. clauses run():- console:init(), initExceptionState(exception:getExceptionState()

53、, trap(MyTxtFileContent=loadFile(myFileName), ErrorCode,handleFileReadError(ErrorCode), !, stdIO:write(Thecontentof,myFileName,is:n,MyTxtFileContent). run().,9.6.3 如何繼續(xù)另一個異常,class predicates handleFileReadError:(exception:traceIDErrorCode)failure . clauses handleFileReadError(ErrorCode):- DescriptorContinued=exception:tryGetDescriptor(ErrorCode,continuedFromDll), Descriptor=exception:tryGetDescrip

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論