在特定位置插入元素

时间:2015-10-14 21:06:01

标签: list prolog

我开始Prolog(仅为我自己),我正在努力进行递归。

我想要一个"方法",它在列表中的特定位置插入一个元素。 到目前为止我尝试的是:

TypeError: element.selectize is not a function

我觉得我很复杂,因为我无法理解递归究竟是如何工作的......

也许有人可以帮助我。

3 个答案:

答案 0 :(得分: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

此外,您通过使用命令性名称(“插入...”)来阻止一个很好的声明性读取。这使得开发关系编程的感觉变得不必要。

因此,我建议你:(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).

为了理解这个程序,我们以声明的方式阅读:第一个句子是 true 如果位置是0,并且最终列表的头部是E,以及尾巴...等。第二个句子是 true 如果 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)符号似乎仍然很奇怪”,您可能希望在程序中使用更广泛使用的印度语 - 阿拉伯语符号表示整数。

为此,我们可以简单地从上面获取任一版本,并通过声明整数算法用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 :(得分:1)

Prolog功能是模式匹配,即基于谓词参数的规则选择。这个特征是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].

答案 2 :(得分: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
).