邏輯式編程還有用嗎?--「三維度」邏輯程式語言的設計(2)
1,邏輯程式語言能做什麼
這兩天正在構思這個「三維度」邏輯程式語言的設計系列的下一篇該怎麼寫,正好在上一篇《用寫文章的方式寫程式--「三維度」邏輯程式語言的設計(1)》有位叫做 dwcz 的朋友回帖說:
「沒啥特點。新出的語言都快實現的功能,還在這裡進行理論構想。邏輯式編程基本被否定了,和函數式有同樣問題--只能在簡單或靜態環境,在複雜或動態環境,造成的問題比要解決的問題還多。」
不知道這位朋友具體是出於什麼原因這麼認為的,我認為一個東西不流行不代表它是失敗的、被否定的事物。邏輯程式語言這幾年的確很少出現在編程社區的討論中,更看不到有關職位的招聘資訊,本著「用腳投票」的原則,認為邏輯程式語言沒什麼用,已經被否定、被淘汰的觀點就有市場了,但是這種觀點是不正確的,流行的東西不一定是好東西,比如流感;不流行的東西只不過很小眾,只要它存在就有它的一小片天地。邏輯程式語言的確很小眾,它有它特殊的應用領域, dwcz 恰恰說反了,邏輯程式語言主要的用武之地就是複雜和動態的環境,這個可以從邏輯程式語言Visual Prolog的官網介紹看到:
//www.visual-prolog.com/default-chinese.htm
使用Visual Prolog可以構建工業級的商用程式,尤其適合處理複雜的認知問題。
Prolog語言開發中心業已證實,以下項目中的先進資源調度和決策支援系統完全是用Visual Prolog編寫的:
員工規劃
機場決策支援
航空公司決策支援
車間調度
基於語音的解決方案
CrewWatch
Ra
上面一段介紹的詳細內容請點擊上面的鏈接了解。而函數式語言跟邏輯程式語言有同樣問題這個說法更不對,函數式語言的鼻祖LISP至今還在,基於JVM的LISP方言Clojure還能常常出現在招聘資訊中,還有比Common LISP更為簡單的方言Scheme,它有一個完善IDE環境的DrRacket實現,還有編譯和運行速度比C還快的Chez Scheme,詳情可以看看知乎對此的討論,也可以看看王垠寫的這篇《揭秘Chez Scheme》(王垠自己的部落格已經加密,這裡貼的是轉載的文章)。我使用Chez Scheme編寫了一個新冠病毒感染風險監測程式,可以點擊我這篇文章《Scheme語言實例入門--怎樣寫一個「新型冠狀病毒感染風險檢測程式」 》了解和下載運行這個Scheme程式。此外,常用的函數式語言還有scala, erlang, F#, Haskell等,說到.NET就不能不提到F#,.NET 5.0將伴隨C#9.0同步發布最新的F# 5.0,可見F#在軟微開發語言中的重要地位。奇怪的是,F#在中國鮮有使用,而在國外被稱為薪水最高的程式語言,如下圖2019程式語言薪資排行榜。
從上面的程式語言排行榜上可以看到,Top 3的語言有兩名都是函數式語言,Erlang都能排名No.9,可見,函數式語言是名副其實的「高薪語言」,還能說函數式語言「只能在簡單或靜態環境」,而不能運用在複雜或動態環境的環境嗎?沒有哪個老闆會在那種「簡單或者靜態環境」的軟體開發項目中給程式設計師高新吧?回歸主題,本篇文章是討論邏輯程式語言的,上面說這麼多,就是要告訴大家,邏輯式編程的重要性,它尤其適合處理複雜的認知問題,解決複雜的業務問題,同樣能夠構建工業級的商業軟體。
2,從一個遊戲認識邏輯編程
前面說了邏輯程式語言的重要性,簡單介紹了Visual Prolog這個邏輯程式語言的用途,但對於習慣了「命令式編程」的程式設計師來說可能對於邏輯程式語言還是沒有概念。
Prolog 語言是以一階謂詞邏輯演算為原理設計的電腦程式語言,在人工智慧的發展 歷程中被寄予厚望,曾經被成為「第五代電腦語言」。Prolog 的程式結構就是事實、規則 和問題,它內置一個推理機,通過輸入事實,處理規則,求解問題。因此它跟其它程式語言 都不同,大部分都是命令式的,而 Prolog 是陳述式的,因此不需要告訴 Prolog 程式的執行 順序即可求解問題。
比如看下面的Prolog程式例子:
likes(bell, sports). % Bell 喜歡運動 likes(mary, music). % Mary 喜歡音樂 likes(mary, sports). % Mary 喜歡運動 likes(jane, reading). % Jane 喜歡閱讀 friend(john, X) :- likes(X, reading), likes(X, music). % 成為 john 的朋友需要喜歡 閱讀和音樂 friend(john, X) :- likes(X, sports), likes(X, music). % 成為 john 的朋友需要喜歡 運動和音樂 ?- friend(john, Y). %誰是 john 的朋友?
運行此程式,將獲得答案:
Y= mary
上面的程式碼第1-第4行,都在說關於「喜歡」什麼的事實;第5、第6行,分部定義了成為某個人的朋友的條件,這些條件成為一套規則;最後一行程式碼是提問。運行Prolog程式後,Prolog內置的「推理機」回溯程式定義的事實,匹配定義的規則,將問題代入這些事實和規則進行消解,最後匹配出答案。(有關這個過程的理解,推薦大家看看《邏輯式程式語言極簡實現(使用C#) - 1. 邏輯式程式語言介紹》這篇文章,作者寫得詼諧有趣,淺顯易懂。)
上面有關Prolog的介紹節選自本人的新書《SOD框架「企業級」應用數據架構實戰》【2.1.6 數據、資訊和知識】一小節的《第三,什麼是知識(Knowledge)?》的內容,已購書的朋友可以看看書中相關內容更多的介紹。
也許上面這個示常式序的運行結果有點」費腦子「,並且這個程式是原生的Prolog程式,不喜歡這種語法風格。沒關係,我們只要明白Prolog語言就是由事實、規則和問題組成的就行了。下面我們再通過一個簡單一點的「遊戲人生」程式來帶領大家認識邏輯編程,並且示例程式碼使用大家熟悉的C#語言來模擬Prolog程式的風格,這樣來看邏輯編程就會自然點。編程之前,我們都會做需求分析,產品經理會給我們講一個」用戶故事「,在這個遊戲中這是兩口子決策是否要生孩子的故事。
故事內容:
- 有一個姑娘很漂亮,美如貂蟬;
- 有一個小伙年輕有為,名叫張三;
- 張三是一個打工仔;
- 貂蟬是張三的妻子;
- 張三是貂蟬的丈夫;
- 貂蟬還沒有生孩子;
- 丈夫可以努力工作打工賺錢;
- 妻子過了35歲生孩子就晚了;
- 丈夫要孩子不能超過60歲,且存款不能小於1萬元;
- 張三努力打工。
- 張三和貂蟬現在可以生孩子了嗎?
在這個故事中,第1條-第6條,以及第10條敘述的是故事男女主角已有的「事實」,第7-9條定義的是家庭中有關生孩子的「規則」,第11條提出問題。事實一經發生就不可改變,事實可以是一些對象之間的關係,也可以是對象的行為,比如這裡說貂蟬是張三的妻子。規則是一些強制性約束,比如社會性的、生理性的或者法律上的,一般也不可以隨意改變。這裡定義的規則只有合法成為夫妻才可以生孩子,所以需要先描述男女主角是夫妻關係。當然不結婚也可以生孩子,但這不是本程式考慮的規則。根據事實和規則,我們就能回答一些問題了,這裡的問題是男女主角何時能夠生孩子。
下面,用C#程式碼來表示這個故事有關的事實、規則和問題:
Woman diaochan = new Woman() { Name = "貂蟬", Birthday = new DateTime(1990, 1, 2) }; Man zhangsan = new Man() { Name = "張三", Birthday = new DateTime(1988, 3, 5) }; Worker worker1 = new Worker(zhangsan); Wife wife1 = new Wife(diaochan,zhangsan); Husband husband1 = new Husband(zhangsan,diaochan); diaochan.ChildrenCount = 0; diaochan.ActAs<Wife>().Child_bearing(); //生孩子 zhangsan.ActAs<Husband>().Money += zhangsan.ActAs<Worker>().Work(); zhangsan.ActAs<Husband>().Child_rearing(); //生孩子
請看上面這個程式碼,基本上和我們的故事「劇本」描述的一模一樣,只不過,生孩子是妻子和丈夫兩個人的事情,「一頭熟」可生不下孩子,所以對象diaocan和zhangsan都可以調用生孩子的方法Child_rearing() ,但是他們兩個人真的能生孩子嗎?這裡不得不提出一個嚴肅而認真的問題:生孩子不是小事,它要看情況。這個「看情況」講的就是一個環境、時機、條件等等,比如是否符合中國有特色的「計劃生育制度」,是否符合優生優育,物質條件是否足夠,心理有沒有做好準備。。。在本文中,我將這種「看情況」有一個正式的詞語來表達--場景,在當前的遊戲人生故事中,這裡的問題就是「生育場景」中的問題。
3,「三維度」邏輯編程
在「三維度」理論中,場景就是有角色參與的,角色在其中進行交互活動的環境。場景因為有角色參與才有意義,角色因為有場景的存在才能發揮角色的行為。在角色與場景的交互過程中,角色和場景的改變可能會誕生新的角色、產生新的場景,而這種變化可以體現在時間維度上。所以三維度理論中的角色、場景和時間是相互影響、緊密相關、不可或缺的關係,具體內容可以參考我之前的文章《業務分析三維度(場景+角色+時 間)之程式設計師坐禪論道》。用三維度理論可以可以很好的描述我們這個遊戲人生故事中的生孩子問題,它的角色維度正好可以描述邏輯程式語言中「一階謂詞」,比如上面程式碼中的Worker、Wife、Husband類,這些「謂詞」描述了對象的特徵,或者對象之間的關係,可以表達一些「事實」之間的關係,實現邏輯程式語言中的「謂詞演算」;它的場景維度可以用來構建一組相關的事實,並且表達這些事實相互之間的一套規則,也就是場景規則。場景更像一個劇本中的槽,這是專家系統中有關知識表達的高級話題,在此先不予討論。
所以,要解決當前遊戲中男女主角是否能生孩子的問題,我們的程式還必須引出「場景」對象,定義一個生育場景,它包括一套有關生孩子的規則,這個規則不同於丈夫、妻子角色自身的規則,前者是社會性、法律法規性質的約束;後者是生理性、心理性的約束。比如中國之前的計劃生育制度,要求一對夫妻只能生育一個孩子;現在的制度是可以生兩個。因此,場景規則隨著時間的推移也可以是可以改變的,脫離時間維度去看場景問題是不對的,同樣脫離時間維度去看角色問題也不對,比如我們的故事中男女主角都會隨著時間的變化而增大年齡,有可能過了最佳生育年齡。這裡可以總結出,角色對象有角色固有的規則,場景對象也有場景的規則。
假定我們已經定義了一個生育場景對象,我們就可以將男女角色放入生育場景,開始我們故事的排練了,而這個就是我們故事要寫的劇本。為了簡化劇本,下面的程式碼不再重複前面已經定義的事實,直接簡化為下面的過程:
- 進入場景。
- 生孩子?
- 努力工作賺錢。
- 生孩子?
- 結束場景。
對應的程式碼如下:
Console.WriteLine("-----開啟【生育場景】規則測試-------------"); diaochan.ChildrenCount = 0; husband1.Money = 5000; ProcreateContext context = new ProcreateContext(new DateTime(2010, 1, 1), zhangsan, diaochan); context.StartContext(); //場景參與人開始扮演角色 diaochan.ActAs<Wife>().Child_bearing(); zhangsan.ActAs<Husband>().Money += zhangsan.ActAs<Worker>().Work(); zhangsan.ActAs<Husband>().Child_rearing(); //啟動規則匹配 Console.WriteLine(); bool rulesFlag = context.MatchRules(); Console.WriteLine("{0} 結果:{1}", context.Name, rulesFlag); Console.Read();
運行這段程式,即可看到張三和貂蟬是否可以生孩子的結果,具體運行結果將在本系列結束後公布,讀者也可以從我的《SOD框架「企業級」應用數據架構實戰》一書中事先看到答案。
4,本篇小節
本篇先討論了什麼是邏輯編程,以及邏輯編程的重要性,然後用一個實例介紹了Prolog這門邏輯程式語言。然後思考邏輯編程的特點,它和我們的「三維度」理論有著天然的契合度。運用三維度理論,我們可以很容易的用一種非邏輯程式語言--C#來實現邏輯編程的範式,這樣我們就能結合邏輯編程的有點以及.NET平台語言強大的功能,從而輕鬆的構建一個新的邏輯程式語言,儘管這隻能稱之為一種DSL,但它也能為一種新的邏輯程式語言的設計提供一個可實現的參考方案。
在下一篇,我們將討論這個「三維度」邏輯程式語言的設計細節,已經購買了我的書的朋友可以先一睹為快。大家有什麼問題可以回帖留言,也方便為我下一篇具體寫作內容提供思路,謝謝大家的支援。