2015-10-14 202 views
3

我開始使用Prolog(僅適用於我自己),而且我正在爲遞歸而苦苦掙扎。在特定位置插入元素

我想要一個「方法」,它將一個元素插入列表中的特定位置。 我試過至今:

insertAt(Element,Position,List,ResultList) 

insertAt(Element,0,L,[Element|L]). 
insertAt(Element,Pos,[E|L],ZL):- 
    Pos1 is Pos-1, 
    insertAt(Element,Pos1,L,ZL), 
    append(E,ZL1,ZL). 

我覺得我挺複雜的,因爲我無法理解如何遞歸函數的具體...

也許有人可以幫助我。

回答

3

你的代碼有幾個特性使得初學者很難理解它。特別是,使用模式化,低級別的算術是一種嚴重的障礙,當與一個有趣的(事實上也是一種嚴肅的)方式與程序交互時。

例如,要了解關係,從最常用的查詢開始很有用。這隻會問「是否有任何解決方案,如果有的話,解決方案是什麼樣的?」。在您的具體的例子,最一般的查詢是這樣的:

?- insertAt(E, Pos, Ls0, Ls). 

,這幾乎立即產生一個instantiation error由於非陳述性算術謂詞使用的是:

?- insertAt(E, Pos, Ls0, Ls). 
Pos = 0, 
Ls = [E|Ls0] ; 
ERROR: insertAt/4: Arguments are not sufficiently instantiated 

此外,你是通過使用命令名稱(「insert ...」)來阻止一個很好的聲明式閱讀。這使得開發關係型編程的感覺不必要的困難。因此,我建議你:(1)使用更具說明性的謂詞名稱,(2)使用自然數的符號表示法,使謂語更容易理解和更一般化。

我將使用數字0表示零,術語s(X)表示數字X的後繼數字。有關此表示的更多信息,請參見

有了這些變化,代碼變爲:

element_at(E, 0, [_|Ls], [E|Ls]). 
element_at(E, s(X), [L|Ls0], [L|Ls]) :- 
     element_at(E, X, Ls0, Ls). 

要了解這個程序,我們聲明閱讀:第一句是如果位置是0,最後頭清單E,尾巴......等等。第二句話是如果element_at(E, X, Ls0, Ls)成立,頭...等

得很漂亮,最一般的查詢現在的效果要好得多:

 
?- element_at(E, Pos, Ls0, Ls). 
Pos = 0, 
Ls0 = [_G1071|_G1072], 
Ls = [E|_G1072] ; 
Pos = s(0), 
Ls0 = [_G1073, _G1079|_G1080], 
Ls = [_G1073, E|_G1080] ; 
Pos = s(s(0)), 
Ls0 = [_G1073, _G1081, _G1087|_G1088], 
Ls = [_G1073, _G1081, E|_G1088] . 

注意,雖然有一些不公平的事情在這裏:如果是其餘位置的答案?爲了更公平的枚舉,我們用length/2,事先說明,我們正在考慮一個又一個列表的長度:

 
?- length(Ls0, _), element_at(E, Pos, Ls0, Ls). 
Ls0 = [_G1124], 
Pos = 0, 
Ls = [E] ; 
Ls0 = [_G1124, _G1127], 
Pos = 0, 
Ls = [E, _G1127] ; 
Ls0 = [_G1124, _G1127], 
Pos = s(0), 
Ls = [_G1124, E] . 

,現在它更清楚如何在不同的參數進行交互,因爲你已經看到條款的各種例子說由謂詞描述。


事實上,以減少我們需要跟蹤的參數的個數和變量名,我描述列表時經常使用DCG notation,我想向你展示這種替代版本太多:

element_at(Element, 0, [_|Ls]) --> 
     [Element], 
     list(Ls). 
element_at(Element, s(X), [L|Ls]) --> 
     [L], 
     element_at(Element, X, Ls). 

list([]) --> []. 
list([L|Ls]) --> [L], list(Ls). 
 
?- length(Ls0, _), phrase(element_at(E, Pos, Ls0), Ls). 
Ls0 = [_G1148], 
Pos = 0, 
Ls = [E] ; 
Ls0 = [_G1148, _G1151], 
Pos = 0, 
Ls = [E, _G1151] ; 
Ls0 = [_G1148, _G1151], 
Pos = s(0), 
Ls = [_G1148, E] . 

一旦你閱讀了表示法,這個版本將會變得清晰。


最後,你可能會說:「嗯,這是很好的,但s(X)符號似乎仍然很奇怪」,你可能想使用整數更廣泛使用的印度 - 阿拉伯數字在你的程序中。爲此,我們可以簡單地從上面的任意一個版本中取代s(X)表示法,並用CLP(FD)約束條件的聲明性整數算術替代s(X)表示法。例如,第一個版本:

:- use_module(library(clpfd)). 

element_at(E, 0, [_|Ls], [E|Ls]). 
element_at(E, X, [L|Ls0], [L|Ls]) :- 
     X #> 0, 
     X #= X0 + 1, 
     element_at(E, X0, Ls0, Ls). 

這也適用於所有的方向,正是因爲我們從一個很好的陳述和一般謂詞期待:

 
?- length(Ls0, _), element_at(E, Pos, Ls0, Ls). 
Ls0 = [_G2095], 
Pos = 0, 
Ls = [E] ; 
Ls0 = [_G2095, _G2098], 
Pos = 0, 
Ls = [E, _G2098] ; 
Ls0 = [_G2095, _G2098], 
Pos = 1, 
Ls = [_G2095, E] . 

請參閱更多的信息,我希望這篇文章鼓勵你更多地關注你的Prolog代碼,嘗試在各個方向上,並且以聲明的方式閱讀它。 (正在描述什麼?)

1

序言特徵是模式匹配,即基於謂詞參數的規則選擇。這種功能對於Prolog符號很關鍵,可以對關係進行簡潔的描述,特別是對於遞歸術語(如列表)。請注意,列表僅僅是用於遞歸術語的「語法糖」,用傳統函數(術語「名稱」,用日常用語)表示。

insertAt(Element,0,L,[Element|L]). % ok 
insertAt(Element,Pos,[E|L],[E|ZL]):- % you forgot to cons back E 
    Pos1 is Pos-1, 
    insertAt(Element,Pos1,L,ZL). % done, append is useless 
    %append(E,ZL1,ZL). 

SWI-Prolog有NTH1/4和nth0/4,可以執行插入:

?- nth0(1,L,x,[1,2,3]). 
L = [1, x, 2, 3]. 
+1

+1。使用'nnth0/4'的解決方案可能是實踐中應該做的。它也可以像我預期的任何參數實例化模式那樣運行。 – 2015-10-15 10:24:11

1

same_length/2append/3length/2採取遞歸的照顧!

insertAt(E,N,Xs,Ys) :- 
    same_length ([E|Xs],Ys), 
    append (Before,Xs0,Xs), 
    length (Before,N), 
    append(Before,[E|Xs0],Ys). 

樣品查詢:

 
?- insertAt(X, N, [a,b,c,d,e], Ys). 
( N = 0, Ys = [X,a,b,c,d,e] 
; N = 1, Ys = [a,X,b,c,d,e] 
; N = 2, Ys = [a,b,X,c,d,e] 
; N = 3, Ys = [a,b,c,X,d,e] 
; N = 4, Ys = [a,b,c,d,X,e] 
; N = 5, Ys = [a,b,c,d,e,X] 
; false 
).