[英]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
您可以將其理解為“如果Body
是Head
”,或“ 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 謂詞。
為什么我們需要一個工人謂詞? 好吧,因為在這里我們想使用另一個變量來計算我們A
和B
項。 但是我們不能用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
這里H
是1
所以我們已經知道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.