簡體   English   中英

了解 Swi-prolog 中的拆分

[英]Understanding the splitting in Swi-prolog

我有將輸入列表分成兩半的代碼。 好像還行。

halve(List,A,B) :- halve(List,List,A,B), !.
halve(B,[],[],B).
halve(B,[_],[],B).
halve([H|T],[_,_|T2],[H|A],B) :-halve(T,T2,A,B). 

好的,所以我嘗試對其進行解碼。 開頭很明確:

“Halve take list and 2 logic variables”是這樣的:

halve(List,A,B) 

(1) 然后繼續這部分:

:- halve(List,List,A,B).

這意味着,我正在從第一個列表或什么創建新的兩個列表(列表、列表)? 究竟是什么代表“:-”? 我猜新的 lists = halves 將是 A 和 B,對吧?

(2) 其次,拜托,我不太明白這兩行:

halve(B,[],[],B).
halve(B,[_],[],B).

也許你可以用一些例子來解釋它,好嗎?

(3) 好吧,我希望在你對(1)和(2)的解釋之后,我會自己得到最后的部分......

halve([H|T],[_,_|T2],[H|A],B) :- halve(T,T2,A,B). 

非常非常感謝你幫助我。


好的,我們的第一個問題已經有了解決方案。 長話短說,它是這樣工作的:

halve([1,2,3,4,5],[1,2],[3,4,5]). 
->true

如果您注意到它將列表分成兩半,但如果列表中的元素數量為奇數,則后半部分較大。

現在我想要獲得的是讓第一個更大

所以我在想這個:

我要做到這一點:

Halves_div([1,2,3],A,B).
A=[1,2],
B=[3].

假設我的輸入是列表:[1,2,3]。 所以我將從拆分列表的頭部和尾部開始: [H|T]然后我將H與新的空列表合並 - 我的第一個半 ( A )。 之后我有 A=[1]、B=[] 和 Input=[2,3]。

對於合並我有:

merge([],List,List).
merge([H|T],List,[H|New]) :- merge(T,List,New).

還有一件事 - 我需要檢查第一半是否已經 >= 第二半,對嗎?

所以這是我的想法,我唯一希望你能幫助我的就是把它寫在 prolog 中。我有點困惑如何把它放在一起。

謝謝!


看來我的解決方案想法太復雜了,我找到了更好的東西!

首先,Prolog 子句如下所示:

Head :- Body

您可以將其理解為“如果BodyHead ”,或“ Body暗示Head ”。

請注意,有時您只需要

Head

那是因為 Head 總是true 在這種情況下,我們不稱Head為子句,而是稱其為事實。

所以在這里,我們有:

halve(List,A,B) :- halve(List,List,A,B).

這意味着如果 halve(List, List, A, B halve(List, A, B)為真,則halve(List, List, A, B)為真。 具體來說,這只是一種halve/3的工作委托給halve/4的方法,即所謂的 worker 謂詞。

為什么我們需要一個工人謂詞? 好吧,因為在這里我們想使用另一個變量來計算我們AB項。 但是我們不能用halve/3做到這一點,因為halve/3 /3 的 3 個參數點已經被輸入列表List 、結果的前半部分A和結果的后半部分B占用。

關於List, List的東西,這只是一種說法,我們用相同的第一個和第二個參數調用halve/4 ,就像在任何編程語言中一樣。

然后有趣的事情開始了。 Prolog 將嘗試證明 halve halve/4對於某些給定的 arguments 是正確的。讓我們以這種方式說明我們稱為 halve halve/3的執行:

?- halve([1, 2], A, B).

然后,如果您按照我之前所說的進行操作,Prolog 現在將嘗試通過以下 arguments 證明 halve halve/4為真來證明 halve halve/3為真: halve halve([1, 2], [1, 2], A, B). .

為此,Prolog 有 3 個選擇。 第一個選擇是以下子句:

halve(B,[],[],B).

顯然,那是行不通的。 因為當 Prolog 試圖通過統一將調用者的第二個參數“放入”被調用者的第二個參數時,它會失敗。 因為[1, 2]不能與[]統一。

只剩下兩個選擇,下一個是:

halve(B,[_],[],B).

同樣,Prolog 無法統一[1, 2][_] ,因為_只是一個變量(如果您遇到問題,請參閱關於匿名變量_的帖子)。

因此 Prolog 必須找到解決您提出的問題的唯一機會是最后一個子句,即:

halve([H|T],[_,_|T2],[H|A],B) :- halve(T,T2,A,B).

在這里,Prolog 會找到統一事物的方法,讓我們看看是哪種方法:

  • 我們必須將[1, 2][H|T]統一起來。 這意味着H = 1.T = [2].
  • 我們必須將[1, 2][_,_|T2]統一起來。 這意味着T2 = [].
  • 現在我們開始使用下一個統一來構建我們的結果,即A = [H|A'] (我為第二個A打底,因為變量在局部范圍內並且它們不相同)。 這里我們告訴我們,當我們從子句的主體計算出結果時,我們將向其添加H 這里H1所以我們已經知道A的第一個元素將是1

ok ok,統一成功,太好了。 我們可以繼續處理該條款的主體。 它只是用這些值(上面計算的)以遞歸方式調用halve/4

halve([2], [], A, B).

在這里,我們重新開始。 雖然這次事情會很快,因為首選 Prolog 非常適合:

halve(B,[],[],B).

可以統一到

halve([2], [], A, B).

使用這些值: A = []B = [2]

所以這是很好的一步,我們現在達到了遞歸的“基本情況”。 我們現在只需要從下到上構建我們的結果。 還記得我們在上面的幾步中遞歸調用我們的謂詞halve/4嗎? 我們已經說過A的第一個元素是1 現在我們知道尾巴是[]所以我們可以 state 那A = [1] 我們沒有說明任何關於B的特別之處,因此B = [2]作為結果保持不變。

既然我詳細介紹了執行過程,您可能想知道,為什么這樣做有效? 好吧,如果你注意的話,你會注意到halve/4的第二個參數的處理速度是第一個參數的兩倍。 [H|T][_, _|T2] 這意味着當我們用第二個參數到達列表的末尾時,第一個參數仍在列表的中間。 這樣我們就可以把東西分成兩部分。

我希望我能幫助您抓住這里工作中的一些微妙之處。

halve(List,A,B) List半部分復制到A並將后半部分與B統一

當我們的列表長度為偶數時,這將是正確的: halve(B,[],[],B).

當 out 列表的長度為奇數時,這將是正確的: halve(B,[_],[],B).

halve([H|T],[_,_|T2],[H|A],B) :- halve(T,T2,A,B).

在這里我們設置 2 讓我們在每個步驟中稱它們為“指針”,我們將一個元素從列表的開頭復制到A因為我們想要獲得前半部分。
因為在每個步驟中我們都從列表中刪除 2 個元素[_,_|T2]當列表只有一個剩余元素或為空時謂詞將停止,然后它將列表的 rest 與B統一。 如果你不能理解使用trace/0

這個版本可能有用...

split_in_half(Xs, Ys, Zs) :-  length(Xs, Len),
Half is Len // 2,    % // denotes integer division, rounding down
split_at(Xs, Half, Ys, Zs).

split_at(Xs, N, Ys, Zs) :- length(Ys, N), append(Ys, Zs, Xs).

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM