是的,但你必須擔心你的謂詞失敗。如果可以,列表中的其餘元素將不會被處理,因爲它會產生連接而不是失敗驅動的循環。
我會更熱衷於使用maplist/2
,因爲我認爲它比foreach/2
更廣泛使用,但我之前也沒有看到過這個選項。 :)
編輯:讓我們來討論我的意思是關於故障驅動循環。
在Prolog中有兩種基本的迭代方法:遞歸和故障驅動循環。假設我想打印列表中的每個項目。遞歸方法是要看起來像這樣:
print_all([]).
print_all([X|Rest]) :- write(X), nl, print_all(Rest).
所以給喜歡[1,2,3]
名單,這是要擴大,像這樣:
print_all([1,2,3])
write(1), nl, print_all([2,3])
write(1), nl, write(2), nl, print_all([3])
write(1), nl, write(2), nl, write(3), nl, print_all([])
write(1), nl, write(2), nl, write(3), nl.
這是member/2
怎麼通常被實現:
member(X, [X|_]).
member(X, [_|Xs]) :- member(X, Xs).
所以你可以看到遞歸方法非常簡單和一般。
另一個簡單但有點皺眉的方法是模擬無法使用回溯機制。這就是所謂的故障驅動迴路,看起來像這樣:
print_all(List) :- member(X, List), write(X), nl, fail.
print_all(_).
當您運行此版本的print_all/1
,發生的事情是有點不是簡單的擴大更加複雜。
print_all([1,2,3])
member([1,2,3], 1)
write(1), nl
fail
retry member([1,2,3], 2)
write(2), nl
fail
retry member([1,2,3], 3)
write(3), nl
fail
retry print_all(_)
true
口頭的fail
力量序言備份到最後選擇點它製成,嘗試使用一個解決方案。那麼,write/1
和nl/0
不會產生選擇點,因爲他們只有一個解決方案,但member/2
確實有多個解決方案 - 一個用於列表中的每個項目。所以Prolog從列表中取出每個項目並打印出來。最後,當member/2
用完解決方案時,Prolog備份到之前的選擇點,這是print_all/1
謂詞的第二個主體,它始終成功。所以輸出看起來一樣。我認爲現在人們普遍寧願不使用失敗驅動的循環,但我不能很好地理解這些論點,以便有用地鸚鵡。
有一件事可以幫助你看到發生了什麼是使用trace
謂詞,並逐步擴展兩個版本,並看看你能否理解這些差異。我上面的記號完全是爲了這個答案而編寫的,可能不太清楚。
回首我原來寫您的實際問題:
foreach
將是確定性的
member
總是爲了迭代,因爲列表中,這樣你必須訪問的方式定義每個項目輪流
而且,這些天至少在SO你會得到很多人告訴你使用maplist
之流,所以它可能不只是工作,但也是一個好主意。
我看到如此建立一個列表,其中我想要所有的解決方案,然後maplist通過循環打印them.I認爲將需要一個故障驅動循環在那裏某處因爲我的程序是非常基於證據的而不是基於計算的。 – codeshot
無論如何應該始終可以做到這一點。如果可以的話,我建議避免失敗驅動的循環,但如果它更合理,請使用它。 –
我對maplist/2的文檔的閱讀是,列表可以重新排序,這意味着動作將以任意順序執行。這意味着它不能解決問題。 遞歸增加了複雜性,沒有描述我的意圖,但我認爲foldl/4以合理的描述性方式實現我需要的東西,如果我只是提供一個適用的墊片,接受兩個額外的參數來累積任何東西:act(A,_, _):call(A) – codeshot