我想知道如何从列表中移动前N个元素并将它们放在最后。 例如:
[1,2,3,4]
我希望移动前2个元素,因此结果将是[3,4,1,2]
。
rule(List1,N,List2) :- length(List1,Y), ...
我不知道如何开始,有什么建议吗?
答案 0 :(得分:3)
既然我们说的是谓词 - 即参数之间的真实关系 - 而Prolog库内置函数在编写时考虑了效率和通用性,你应该知道 - 例如 - length / 2可以生成 a列表,以及'测量'它的长度和追加/ 3也可以拆分两个列表。然后,您的任务可能是
'move first N elements to the end of the list'(L,N,R) :-
length(-,-),
append(-,-,-),
append(-,-,-).
用适当的变量替换每个短划线,您将获得
?- 'move first N elements to the end of the list'([1,2,3,4],2,R).
R = [3, 4, 1, 2].
答案 1 :(得分:3)
您可以选择对该任务采用更一般的观点。如果你考虑一下,取一个列表的前N个元素并在末尾附加它们可以看作是向左旋转N个步骤(想象一下列表元素排列成一个圆圈)。 @Willem Van Onsem的回答中的谓词名rotate/3
也表明了这一观点。实际上,您可以将这样的谓词定义为真正的关系,即使其在所有方向上都能正常工作。另外,希望避免对参数施加不必要的限制,同时保留良好的终止属性。为了反映谓词的关系性质,我们选择一个描述性名称。由于第三个参数是作为第一个参数的列表的N
步骤的左旋转,所以我们可以将其称为list_n_lrot/3
并将其定义为:
:- use_module(library(lists)).
:- use_module(library(clpfd)).
list_n_lrot([],0,[]). % <- special case
list_n_lrot(L,N,LR) :-
list_list_samelength(L,LR,Len), % <- structural constraint
NMod #= N mod Len,
list_n_heads_tail(L,NMod,H,T),
append(T,H,LR).
list_list_samelength([],[],0).
list_list_samelength([_X|Xs],[_Y|Ys],N1) :-
N1 #> 0,
N0 #= N1-1,
list_list_samelength(Xs,Ys,N0).
list_n_heads_tail(L,N,H,T) :-
if_(N=0,(L=T,H=[]),
(N0#=N-1,L=[X|Xs],H=[X|Ys],list_n_heads_tail(Xs,N0,Ys,T))).
现在让我们逐步完成定义并通过示例观察它的一些效果。 list_n_lrot/3
的第一条规则仅用于处理空列表的特殊情况:
?- list_n_lrot([],N,R).
N = 0,
R = [] ;
false.
?- list_n_lrot(L,N,[]).
L = [],
N = 0 ;
false.
?- list_n_lrot(L,N,R).
L = R, R = [],
N = 0 ;
...
如果您不想在解决方案中包含这些案例,请忽略该规则。在整个谓词中,CLP(FD)用于算术约束,因此list_n_lrot/3
的第二个参数可以是变量而不会导致实例化错误。目标list_list_samelength/2
是一个结构约束,以确保两个列表具有相同的长度。这有助于在产生所有答案之后避免无限循环,只有第三个参数是基础的(要看到这个,删除list_n_lrot/3
的前两个目标并用list_n_heads_tail(L,N,H,T)
替换第三个目标,然后尝试查询?- list_n_lrot(L,N,[1,2,3]).
)。这也是为什么最常见的查询以公平的顺序列出解决方案的原因,即为每个列表长度生成所有可能性,而不是仅按0步列出轮换:
?- list_n_lrot(L,N,R).
... % <- first solutions
L = R, R = [_G2142, _G2145, _G2148], % <- length 3, rotation by 0 steps
N mod 3#=0 ;
L = [_G2502, _G2505, _G2508], % <- length 3, rotation by 1 step
R = [_G2505, _G2508, _G2502],
N mod 3#=1 ;
L = [_G2865, _G2868, _G2871], % <- length 3, rotation by 2 steps
R = [_G2871, _G2865, _G2868],
N mod 3#=2 ;
... % <- further solutions
最后,它还描述了两个列表的实际长度,这些列表在下一个目标中用于确定N
的余数,以模块列表的长度为模。请考虑以下事项:如果您将长度为N的列表旋转N步,则会再次到达初始列表。因此,按N + 1步进行旋转会产生与旋转1步相同的列表。从代数的角度来说,这个目标正在利用同余模N的事实将无限整数集划分为有限数量的残差类。因此,对于长度为N的列表,足以产生对应于N个残差类别的N个旋转,以便覆盖所有可能的旋转(参见上面的查询,N = 3)。另一方面,给定的 N>列表长度可以通过取其残差类的最小非负成员来轻松计算。例如,给定一个包含三个元素的列表,分别通过2或5步旋转产生相同的结果:
?- list_n_lrot([1,2,3],2,R).
R = [3, 1, 2].
?- list_n_lrot([1,2,3],5,R).
R = [3, 1, 2].
当然,您也可以通过负数旋转列表,将其向另一个方向旋转:
?- list_n_lrot([1,2,3],-1,R).
R = [3, 1, 2].
在旁注:由于这构成向右旋转,您可以通过简单地写下来轻松定义正确的旋转:
list_n_rrot(L,N,R) :-
list_n_lrot(L,-N,R).
?- list_n_rrot([1,2,3],1,R).
R = [3, 1, 2].
谓词list_n_heads_tail/4
与Willem的帖子中的splitAt/4
非常相似。但是,由于if_/3的使用,谓词在确定性上成功(在唯一的答案之后无需点击;
,因为没有不必要的选择点被打开),如果其中一个列表和{的第二个参数{ {1}}是基础:
list_n_lrot/3
您可以观察到使用CLP(FD)与最常见查询的第二个解决方案的另一个好效果:
?- list_n_lrot2([a,b,c,d,e],2,R).
R = [c, d, e, a, b].
?- list_n_lrot2(L,2,[c,d,e,a,b]).
L = [a, b, c, d, e].
这个答案指出,对于具有一个元素的列表,通过任意数量的步骤进行任何旋转都会再次产生相同的列表。所以原则上,这个单一的一般性答案总结了无数的具体答案。此外,您还可以提出以下问题:通过2个步骤进行旋转的列表是什么?
?- list_n_lrot(L,N,R).
L = R, R = [],
N = 0 ;
L = R, R = [_G125], % <- here
N in inf..sup ; % <- here
...
最后回到你问题中的例子:
?- list_n_lrot2(L,2,R).
L = R, R = [_G19] ;
L = R, R = [_G19, _G54] ;
L = [_G19, _G54, _G22],
R = [_G22, _G19, _G54] ;
...
请注意,在列表上定义任意旋转的这种更通用的方法包含了将前N个元素重新定位到列表末尾的用例。
答案 2 :(得分:1)
试试这个
despI([C|B],R):-append(B,[C|[]],R).
desp(A,0,A).
desp([C|B],N,R):-N1 is N - 1, despI([C|B],R1), desp(R1,N1,R),!.
第一个谓词将一个元素移动到列表的末尾,然后我唯一要做的就是&#34;重复&#34; N次。
?-de([1,2,3,4],2,R).
R = [3, 4, 1, 2].
?- de([1,2,3,4,5,6,7],4,R).
R = [5, 6, 7, 1, 2, 3, 4].
答案 3 :(得分:1)
我们可以使用分为两个阶段的谓词来完成此任务:
N
项;和让我们用单独的谓词构造两个阶段。对于收集阶段,我们可以使用以下谓词:
% splitAt(L,N,L1,L2).
splitAt(L,0,[],L).
splitAt([H|T],N,[H|T1],L2) :-
N > 0,
N1 is N-1,
splitAt(T,N1,T1,L2).
现在对于发射阶段,我们可以使用append/3
。那么完整的谓词就是:
rotate(L,N,R) :-
splitAt(L,N,L1,L2),
append(L2,L1,R).
这给出了:
?- rotate([1,2,3,4],0,R).
R = [1, 2, 3, 4] .
?- rotate([1,2,3,4],1,R).
R = [2, 3, 4, 1] .
?- rotate([1,2,3,4],2,R).
R = [3, 4, 1, 2] .
?- rotate([1,2,3,4],3,R).
R = [4, 1, 2, 3] .
?- rotate([1,2,3,4],4,R).
R = [1, 2, 3, 4].
该算法适用于 O(n)。