Learn Prolog Now 翻譯 - 第三章 - 遞歸 - 第三節,練習題和答案


練習題3.1

 在之前的章節中,我們已經討論了如下的謂詞邏輯:

  descend(X, Y) :- child(X, Y).

  descend(X, Y) :- child(X, Z), descend(Z, Y).

 假設我們將謂詞邏輯重構如下:

  descend(X, Y) :- child(X, Y).

  descend(X, Y) :- descend(X, Z), descend(Z, Y).

 這會導致問題嗎?

 

 我的答案:

 1. 這個謂詞邏輯是有問題的,因為規則2中存在左遞歸的情況,即規則2的主干部分的第一個目標,和規則2的頭部是相同的函子;

 2. 但是由於規則1是一個非遞歸的謂詞邏輯,所以在進行一些查詢時,能夠根據這個規則進行終止,從而得出結果,比如:

  ?- descend(anne, bridget).

  Prolog會回答true;

  ?- descend(anne, emily).

  Prolog會回答true;

 3. 但是在問一些不能由規則1終止的問題時,Prolog會報"Out of local stack"的錯誤,代表遞歸不能終止,比如:

  ?- descend(bridget, anne).

  ?- descend(anne, X); 一直使用“;”尋找下一個答案,也會報錯

 

練習題3.2 

 知道俄羅斯木偶(又稱為俄羅斯套娃娃)嗎?其中較大的娃娃會包含較小的娃娃,如下圖所示:

 

  首先,寫出一個使用謂詞邏輯directlyIn/2的知識庫,表示木偶直接被另外一個木偶包含。其次,定義一個遞歸的謂詞邏輯in/2,告訴某個木偶是否被另外一個木偶包含

(直接或者間接)。比如,如果查詢in(katarina, natasha),應該回答true,但是in(olga, katarina)應該回答false。

 

 我的答案:

directlyIn(katarina, olga).
directlyIn(olga, natasha).
directlyIn(natasha, irina).

in(X, Y) :- directlyIn(X, Y).
in(X, Y) :- directlyIn(X, Z), in(Z, Y).

 

練習題3.3

 有如下的知識庫:

directTrain(saarbruecken, dudweiler).
directTrain(forbach, saarbruecken).
directTrain(freyming, forbach).
directTrain(stAvold, freyming).
directTrain(fahlquemont, stAvold).
directTrain(metz, fahlquemont).
directTrain(nancy, metz).

 即,這個知識庫記錄了可以直接連通到城鎮。但是,我們可以通過連接不同的城鎮去旅行到更遠的地方。請寫一個謂詞邏輯travelFromTo/2,可以告訴我們如何在這些

城鎮之間通行。比如,如果查詢:

 ?- travelFromTo(nancy, saarbruecken).

 Prolog會回答true。

 

 我的答案:

travelFromTo(X, Y) :- directTrain(X, Y).
travelFromTo(X, Y) :- directTrain(X, Z), travelFromTo(Z, Y).

 

練習題3.4

 定義一個謂詞邏輯greater_than/2,有兩個參數,使用本章中的數字表示方法(比如,numeral(0), numeral(succ(0)), numeral(succ(succ(0)))等),然后判斷第一

個參數是否大於第二個參數,比如:

 ?- greater_than(numeral(succ(succ(succ(0)))), numeral(succ(0))).

 Prolog會回答true;

 ?- greater_than(numeral(succ(succ(0))), numeral(succ(succ(succ(0))))).

 Prolog會回答false;

 

 我的答案:

numeral(0).
numeral(succ(X)) :- numeral(X).

greater_than(numeral(X), numeral(0)) :- X \= 0.
greater_than(numeral(succ(X)), numeral(succ(Y))) :- 
    greater_than(numeral(X), numeral(Y))

 

練習題3.5

 二叉樹是每個內部節點嚴格有兩個子節點的樹形結構。一顆最小的二叉樹僅由一個葉子節點構成。我們使用leaf(Label)代表葉子節點。比如,leaf(3)和leaf(7)都是

葉子節點。假設兩顆二叉樹B1和B2能夠通過謂詞tree/2,合並稱為一顆二叉樹,如下:tree(B1, B2)。那么,從葉子節點開始,我們能夠構建二叉樹:tree(leaf(1),leaf(2)),

類似地,從一顆二叉樹tree(leaf(1),leaf(2))和葉子節點leaf(4),能夠構建出新的二叉樹:tree(tree(leaf(1),leaf(2)), leaf(4))。

 現在,請定義一個謂詞邏輯swap/2,能夠根據第一個參數的二叉樹,構建第二個參數成為其鏡像二叉樹,比如:

 ?- swap(tree(tree(leaf(1),leaf(2)), leaf(4)), T).

 T = tree(leaf(4), tree(leaf(2),leaf(1))).

 true

 

 我的答案和解釋,測試結果如下:

tree(leaf(X), leaf(Y)) :- integer(X), integer(Y).
tree(tree(X1, X2), leaf(Y)) :- tree(X1, X2), integer(Y).
tree(leaf(Y), tree(X1, X2)) :- integer(Y), tree(X1, X2).
tree(tree(X1, X2), tree(Y1, Y2)) :- tree(X1, X2), tree(Y1, Y2).

swap(tree(leaf(X1), leaf(X2)), tree(leaf(X2), leaf(X1))) :-
    integer(X1), integer(X2).
swap(tree(Tree1, leaf(X1)), tree(leaf(X1), Tree2)) :- 
    integer(X1), swap(Tree1, Tree2).
swap(tree(leaf(X1), Tree1), tree(Tree2, leaf(X1))) :-
    integer(X1), swap(Tree1, Tree2).
swap(tree(Tree1, Tree2), tree(Tree3, Tree4)) :- 
    swap(Tree1, Tree4), swap(Tree2, Tree3).

 一些說明:

 1. integer/1謂詞邏輯用於檢查參數是否是一個整數;

 2. tree/2謂詞邏輯定義了二叉樹的邏輯,分為四個子句:子句1定義了兩個節點都是葉子節點的基礎邏輯;子句2定義了左節點是二叉樹,右節點是葉子節點的遞歸邏輯;

   子句3定義了左節點是葉子節點,右節點是二叉樹的遞歸邏輯;子句4定義了兩個子節點都是二叉樹的遞歸邏輯;

 3. swap/2謂詞邏輯定義了二叉樹鏡像實現,方式類似於tree/2的定義。

 4. 下面是一些測試和結果: 

   ?- swap(tree(leaf(1), leaf(2)), T).

   T = tree(leaf(2), leaf(1)) .

   ?- swap(tree(tree(leaf(1), leaf(2)), leaf(4)), T).
   T = tree(leaf(4), tree(leaf(2), leaf(1))) .

   ?- swap(tree(leaf(4), tree(leaf(1), leaf(2))), T).
   T = tree(tree(leaf(2), leaf(1)), leaf(4)) .

   ?- swap(tree(tree(leaf(1), leaf(2)), tree(leaf(3), leaf(4))), T).
   T = tree(tree(leaf(4), leaf(3)), tree(leaf(2), leaf(1))) .

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com