2015-10-14 141 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 
).