你的代碼有幾個特性使得初學者很難理解它。特別是,使用模式化,低級別的算術是一種嚴重的障礙,當與一個有趣的(事實上也是一種嚴肅的)方式與程序交互時。
例如,要了解關係,從最常用的查詢開始很有用。這隻會問「是否有任何解決方案,如果有的話,解決方案是什麼樣的?」。在您的具體的例子,最一般的查詢是這樣的:
?- 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
的後繼數字。有關此表示的更多信息,請參見successor-arithmetics。
有了這些變化,代碼變爲:
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] .
一旦你閱讀了dcg表示法,這個版本將會變得清晰。
最後,你可能會說:「嗯,這是很好的,但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] .
請參閱clpfd更多的信息,我希望這篇文章鼓勵你更多地關注你的Prolog代碼,嘗試在各個方向上,並且以聲明的方式閱讀它。 (正在描述什麼?)
+1。使用'nnth0/4'的解決方案可能是實踐中應該做的。它也可以像我預期的任何參數實例化模式那樣運行。 – 2015-10-15 10:24:11