[英]Prolog: Rules evaluating in an unexpected order
我目前正在開發Prolog程序以從列表中刪除重復項。
這是我的代碼:
makeset([First], [First]). % if only one item in list, the solution is itself.
makeset([First, Second], [First, Second]) :-
dif(First, Second).
makeset([First, Second], [First]) :-
First = Second.
% if more than two items in list
makeset([First | Tail], FinalList) :-
member(First, Tail),
makeset(Tail, FinalList).
makeset([First | Tail], FinalList) :-
not(member(First, Tail)),
makeset(Tail, [First |FinalList]).
但是,當我在包含兩個以上項目的列表上嘗試此操作時,它只返回false。 我跑了一個跟蹤,發現它繼續使用最后兩個規則,即使它在列表中的兩個項目。 為什么是這樣?
示例查詢: makeset([3, 2, 4, 5, 1], X).
我得回假,但它繼續使用最后一條規則進行評估。
編輯:我將規則更改為以下內容,但仍然無法正確評估,因此問題必須與最后一條規則有關。
makeset([First], FinalList) :-
FinalList = [First]. % if only one item in list, the solution is itself.
makeset([First, Second], FinalList) :-
not(First = Second), FinalList = [First, Second].
makeset([First, Second], FinalList) :-
First = Second, FinalList = [First].
% if more than two items in list
makeset([First | Tail], FinalList) :-
member(First, Tail), makeset(Tail, FinalList).
makeset([First | Tail], FinalList) :-
not(member(First, Tail)), makeset(Tail, [First |FinalList]).
我如何修復最終規則?
首先。 看來你正在堅持這個世界的純粹一面。 當然,你使用的是dif/2
! 與其程序對應物相比,它的丑陋兄弟(\\=)/2
意味着在這個時間點不可統一,但可能更晚,或者從不,不要問我 , dif/2
謂詞具有純粹的陳述性含義。 它意味着(句法或理論上的)不平等。 這不僅會給你帶來業力和嗜好。 不,它甚至可以幫助您調試程序。 事實上,我們可以使用Prolog來幫助我們找到程序中的問題 - 如果只有你的代碼足夠純粹! 首先,讓我們凈化您的代碼,然后我們可以調試它。
你的代碼中唯一不行的是not(member(First, Tail))
。 我將用maplist(dif(First),Tail)
替換它maplist(dif(First),Tail)
。 現在我們有一個純粹的程序。 它像以前一樣失敗了。 首先可能是減少列表的大小。 你真的想要完成那件苦差事嗎? 這是一個更簡單的方法,只適用於純Prolog程序。 我會問:
告訴我所有可以想象的列表,他們的設置是什么。
對, 所有列表 。 沒有人會被遺忘。
| ?- length(L,I), makeset(L, X). I = 1, L = [_A], X = [_A] ? ; I = 2, L = [_A,_B], X = [_A,_B], prolog:dif(_A,_B) ? ; I = 2, L = [_A,_A], X = [_A] ? ; I = 2, L = [_A,_A], X = [_A] ? ...
這開始很糟糕! 首先, I = 0
沒有解決方案。 讓我們添加事實makeset([], [])
,然后I = 2
就有這種冗余。 您可以刪除規則makeset([First, Second], [First]) :- First = Second.
現在,讓我們再次詢問查詢:
| ?- length(L,I), makeset(L, X). I = 0, L = [], X = [] ? ; I = 1, L = [_A], X = [_A] ? ; I = 2, L = [_A,_B], X = [_A,_B], prolog:dif(_A,_B) ? ; I = 2, L = [_A,_A], X = [_A] ? ; I = 3, L = [_A,_A,_B], X = [_A,_B], prolog:dif(_A,_B) ? ; I = 3, L = [_A,_A,_A], X = [_A] ? ; I = 3, L = [_A,_B,_A], X = [_B,_A], prolog:dif(_B,_A) ? ; I = 3, L = [_A,_A,_A], X = [_A] ? ; I = 4, L = [_A,_A,_A,_B], X = [_A,_B], prolog:dif(_A,_B) ? ...
I in 0..2
所有答案現在看來都很完美。 它們包含可以想象的所有列表。 他們全部。 請注意,在一個答案中,再次出現dif/2
,確保元素不同。
對於I = 3
事情變得更加復雜。 一些觀察:
X = [_,_,_]
沒有答案。 發現錯誤! 事實上,發現最小的bug!
L = [_A,_A,_A]
有一個多余的答案
我們可以將第一個問題減少到一個目標:
| ?- L = [_,_,_], L = X, makeset(L, X).
no
應該有一個答案。 我們現在可以應用另一種策略:
顯然, makeset/2
的定義過於專業化。 我現在將嘗試概括它以更好地本地化錯誤。 為此,請將以下內容添加到您的程序中:
:- op(950,fy, [*]).
*_.
通過這種方式,我們可以在目標前添加*
以將其刪除。 我們也可以使用注釋,但這是一種更簡潔的方法。 所以我現在要做的是系統地添加*
,然后看看目標是否仍然失敗。 此外,我將一些變量重命名為新名稱( _x
),這相當於進一步概括。 經過一番嘗試,我最終得到:
:- op(950,fy, [*]). *_. makeset([], []). makeset([First], [_xFirst]). makeset([First, Second], [_xFirst, _xSecond]) :- *dif(First, Second). makeset([First | Tail], FinalList) :- *member(First, Tail), makeset(Tail, FinalList). makeset([First | Tail], FinalList) :- *maplist(dif(First),Tail), makeset(Tail, [_xFirst |FinalList]). ?- L = [_,_,_], L = X, makeset(L, X).
在這個程序中,實際元素不再相關。 因此它只是列表的長度! 所以問題必須與處理長度的方式有關。 如果存在第二個參數為更長列表的解決方案,則最后一條規則僅適用。 哪個不可以。 所以問題是最后一條規則。 列表應該在頭部發生。
現在,出現了新的裁員。 最終定義如下:
makeset([], []).
makeset([First | Tail], FinalList) :-
member(First, Tail),
makeset(Tail, FinalList).
makeset([First | Tail], [First|FinalList]) :-
maplist(dif(First),Tail),
makeset(Tail, FinalList).
有更聰明的方式來表達這個member
/ maplist dif
二分法。 但我會這樣離開......
在最后一條規則中,您將元素從第一個列表移動到第二個列表。 然后,您將阻止匹配最高規則。
注意:dif / 2它是一個強大但復雜的謂詞。 你有效地需要它的力量嗎?
makeset([First | Tail], [First | FinalList]) :-
not(member(First, Tail)), makeset(Tail, FinalList).
您不應該將makeset
的輸入的First
部分設置為阻止先前的規則匹配。 您應該只添加First
后到列表FinalList
使用統一makeset
謂語。
當輸入列表為空時,您的解決方案也無法處理該情況。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.