[英]Satisfying a set of goals in Prolog
在Prolog中,我經常通過提供模板(包含變量的結構)然后滿足一組約束來解決問題。 一個簡單的例子可能是:
go(T) :-
T = [_, _, _],
member(cat, T),
member(dog, T),
member(mouse, T).
實際上,約束集是以其他方式生成的,而不是被修復,我必須編寫一個遞歸謂詞來依次滿足每個約束:
go(T) :-
T = [_, _, _],
findall(A, animal(A), As),
% satisy member(A, T) for each A in As
fill_in_animals(T, As)
fill_in_animals(T, []).
fill_in_animals(T, [A|Rest]) :-
member(A, T),
fill_in_animals(T, Rest).
請注意,我的問題不是與列表相關的約束,甚至參數也不能總是容易地生成為要傳遞給上面使用的相對簡單的輔助謂詞的列表。 在實踐中,我發現助手是我每次寫的一個相當笨拙的謂詞,其中:
我正在尋找的是一個基於findall
等的謂詞,它將一個接一個地滿足一系列目標。 就像是:
% satisfyall(:Goal)
% backtracks on Goal but keeps all bindings from each fully satisfied goal.
satisfyall((animal(A), member(A, T)))
我正在尋找的答案不一定是這種形式。 實際上,在回溯目標和維護由此產生的每組綁定之間可能存在矛盾。
我希望我已經解釋了我的問題,以便能夠幫助我們。 (如果不讓我知道的話。)為這個冗長的問題提前道歉!
更新(2年后)
我會在今天晚些時候試一試並更新我的問題!
請注意,我從未說過我會在嘗試的同一天更新問題。 ;-)
@CapelliC引導我朝着正確的方向前進,我發現了一種似乎運作良好的模式:
?- Gs = [member(red),member(blue)], T = [_,_], foreach(member(G, Gs), call(G, T)).
T = [red, blue] ;
T = [blue, red] ;
您在問題中描述的情況與您給出的satisfyall/1
謂詞的簽名略有不同。 fill_in_animals
示例中沒有涉及回溯,至少不涉及流出go/1
的變量。 對子目標的滿意度可能存在“小回溯”,但總體目標不會失敗,同時保持綁定完整。
一個陳腐的,可能沒有幫助的解決方案是使用maplist/2
。 例如,您的示例很容易實現:
?- length(L, 3), maplist(animal, L).
L = [cat, cat, cat] ;
L = [cat, cat, dog] ;
L = [cat, cat, mouse] ;
L = [cat, dog, cat] ;
...
L = [mouse, mouse, dog] ;
L = [mouse, mouse, mouse].
您可以通過添加一個謂詞來繼續使用物化數據庫:
% just flips the arguments of member/2
memberof(L, A) :- member(A, L).
然后我們可以使用findall/3
來完成工作:
?- findall(A, animal(A), Animals),
length(L, 3),
maplist(memberof(Animals), L).
Animals = [cat, dog, mouse],
L = [cat, cat, cat] ;
Animals = [cat, dog, mouse],
L = [cat, cat, dog] ;
Animals = [cat, dog, mouse],
L = [cat, cat, mouse] ;
...
Animals = [cat, dog, mouse],
L = [mouse, mouse, dog] ;
Animals = [cat, dog, mouse],
L = [mouse, mouse, mouse].
這應該清楚為什么lambda.pl會有所幫助。 你不需要幫助器謂詞,你可以簡單地寫:
?- findall(A, animal(A), Animals),
length(L, 3),
maplist(\Animal^member(Animal, Animals), L).
(另)
如果你真的想要繞過變量綁定和取消綁定,我想你會為自己創建一個調試噩夢,但是SWI-Prolog有一個你可以使用的全局變量工具 。 我隱約記得在某處讀取asserta
/ retract
不足以完成這項任務。
我越去想它,越感覺對我來說,有沒有將是一個有意義的實施satisfyall/1
,從實質性的區別maplist/2
,但我期待着找出我錯了。
您肯定知道回溯需要撤消為完成工作所做的更改。 這是Prolog算法的核心,也是Prolog能力的源泉。
當它涉及更常見的計算時,它也是一個弱點,就像那些必然涉及副作用或循環的計算一樣。
找到正確的方法迫使我們的規則在確定性路徑上工作可能很困難,可能是因為這不是Prolog應該工作的方式。
嗯,現在,停止哲學咆哮,讓我們看看Prolog的大師們為我們提供了什么:SWI-Prolog提供圖書館( 聚合 ),你找到了foreach ,我認為你做的很多就是:
?- foreach(animal(X), member(X,L)).
L = [cat, dog, mouse|_G10698] .
研究這樣復雜的內置函數可以讓你對實現有所了解(使用?- edit(foreach).
從控制台來檢查源代碼)。
請注意,它將發電機和目標分開,而在你的問題中它們是無望的聯合。 當然, 這只 需要能夠在發電機部件上回溯。
順便說一句,嘗試理解doc頁面中的小例子列表。 它是由dif / 2過於復雜,但你真的需要掌握綁定的行為,以便能夠推廣你的遞歸謂詞。
HTH
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.