2016-12-01 22 views
0

我有這樣的事實清單:序言 - 停止重複時,最後的事實

set(h, 3). 
set(h, 6). 
set(h, 12). 
set(h, 1). 
set(h, 7). 

我需要找到一套H的最大值和查詢需要看起來像這樣:

?- maximum(h, Max). 
Max = 12. 

顯然,現在有很多方法可以做到這一點。但目前我正在尋找一種方法來實現動態謂詞和失敗謂詞。我可能不得不重複我猜測?不幸的是,重複和動態謂詞似乎讓我感到困惑。

我的想法是這樣的:

set(h, 3). 
set(h, 6). 
set(h, 12). 
set(h, 1). 
set(h, 7). 

:- dynamic current_max/1. 

assert(current_max(0)). 

maximum(Set, Element):- 
    repeat, 
    set(Set,Element), 
    current_max(Max), 
    (Max > Element, 
     fail); 
    (Element > Max, 
    retract(current_max(Max)), 
    assert(current_max(Element)), 
    fail). 

maximum(_, Max):- 
    current_max(Max). 

一個我看到自己是在重複週期不會停止的問題。如果我不知何故知道這是我可能使用剪輯的最後一個元素,但我不知道如何。

回答

2

故障驅動循環如下所示:

( Generator, 
    Side_effect, 
    fail 
; true 
) 

在你的情況set/2是發電機,並更新當前最大的是副作用。在這種情況下,您不需要repeat:調用set/2將生成選擇點。當您必須創建沒有生成器的選擇點時,您需要repeat。我不得不說,這是一個很奇怪的問題(你有asked it before)。如果你想要這樣做,至少不要使用動態謂詞來保持當前的最大值:這只是乞求麻煩。

在SWI-Prolog的library(aggregate)中有這樣做的例子。以aggregate_all/3爲出發點,只留下代碼查找最大的數字,你有:

set(h, 3). 
set(h, 6). 
set(h, 12). 
set(h, 1). 
set(h, 7). 

maximum(Set, Max) :- 
     State = state(Max), 
     (  set(Set, Max), 
       arg(1, State, M0), 
       M is max(M0, Max), 
       nb_setarg(1, State, M), 
       fail 
     ;  arg(1, State, Max), 
       nonvar(Max) 
     ). 

它使用一個詞來保持當前最大,所以你保持狀態是本地maximum/2謂詞。它也不會假定一個初始值(如果你以0開始,並且所有的值都是負數,你將得到一個不正確的結果!)。有了這樣的定義:

?- maximum(h, M). 
M = 12. 

?- maximum(x, M). 
false. 

幾點意見:

您需要的nonvar(Max)在當在脫節的開始調用set/2失敗非常的情況下結束。然而,需要注意的是,因爲它代表的那一刻,你仍然可以得到錯誤的結果:

?- maximum(x, 42). 
true. 

你應該能夠找出如何處理這個問題。

目前,這使用算術函數max來獲得兩個數字中的較大者。如果你還有其他東西,說原子(即使有一個原子定義的總排序),這也不起作用。您可以定義發現的最大任意兩個術語的斷言:

term_max(X, Y, Max) :- 
    ( X @> Y 
    -> Max = X 
    ; Max = Y 
    ). 

然後,您只需更換

M is Max(M0, Max) 

有:

term_max(M0, Max, M) 
+0

男人,我會說實話。這似乎是在另一個層面上,我還有很多東西需要學習(state,arg,nb_setarg,nonvar等)。 是否沒有辦法使它保持最大值的動態謂詞?或者重複一遍。我正在努力處理那些具體的問題。 另外,如果我真的想讓它與原子一起工作呢?所以它會先數字,然後是字母(a-z)?這可能嗎? – PadaKatel

+0

@PadaKatel在這裏你確實不需要'repeat',因爲'set/2'會創建選擇點。在答案的開頭,我展示了失敗驅動循環的外觀,以及如何使用它來解決您的問題;收回+斷言你已經在你的問題是「副作用」。但請記住,按你想要的方式做它本質上是破碎的,只能用於學習目的! – 2016-12-01 15:34:33

+0

嘿。我給另一個評論寫了另一個代碼示例。 – PadaKatel

0

好。我現在已經看了這幾天了。我想我明白爲什麼我們不需要重複。我嘗試將我的(壞)方法與你的一些想法結合起來。

這是我想出了:

maximum(Set, Element):- 
    ((set(Set,Element), 
     current_max(Max), 
     (Max > Element, 
      fail); 
     (Element > Max, 
     retract(current_max(Max)), 
     assert(current_max(Element)), 
     fail)) 
    ; 
    true 
    ). 

maximum(_, Max):- 
    current_max(Max).) 

不幸的是,現在它給了我「論據不夠實例」。

以下是我的想法:set/2生成值(Set可以被賦予它,Element從事實獲得第一個值)。然後它從current_max/1開始取值(最初爲0)。如果max大於失敗,並從set中取出另一個值。如果Element大於Max,我們刪除以前的current_max事實並創建新的。然後,我們也失敗了,然後回去。一旦所有值都通過了,它就會返回true。然後我希望它也會通過第二條規則來給出最大答案。

0

低效,但也許說教有用的解決方案是使用forall/2爲您找到正確的價值:

maximum(Set, Max) :- 
    set(Set, Max), 
    forall(set(Set, Other), 
      Max >= Other). 

的Prolog會(低效率)選擇的每個元素作爲候選Max直到forall/2步驟是滿意,所以這絕對是一個二次時間解決方案,但它在我看來說明了「Prolog思考」比故障驅動循環更好。

repeat/0實際上是Prolog中的一種低級別的機器。大多數情況下,通過邏輯思考您的問題並圍繞該邏輯構建解決方案,您可以獲得更好的結果,而不必擔心Prolog的解決方案系統將如何提高結果。在前一種方法中,您仍然需要了解回溯,以及Prolog如何重試,但您是如何使用它,而不是直接與之對抗或試圖利用它。