做到这一点的一种方法(注意排列是按字典顺序排列的)是明确声明两个连续元素不能相同:
perm(1, Input, [Last]) :-
member(Last, Input).
perm(N, Input, [First,Second|Perm]) :-
N > 1, N0 is N-1,
member(First, Input),
dif(First, Second),
perm(N0, Input, [Second|Perm]).
?- perm(3,[a,b,c],R).
R = [a, b, a] ;
R = [a, b, c] ;
R = [a, c, a] ;
R = [a, c, b] ;
R = [b, a, b] ;
R = [b, a, c] ;
R = [b, c, a] ;
R = [b, c, b] ;
R = [c, a, b] ;
R = [c, a, c] ;
R = [c, b, a] ;
R = [c, b, c] ;
false.
你已经用 swi-prolog 标记了这个问题。它有一个谓词dif/2,就像其他主要的 Prolog 实现一样。您还可以在 StackOverflow 上找到很多关于 dif/2 的有趣讨论,各有优缺点。总而言之,它提出了一个约束,它的两个论点无法统一。它可以用于未实例化的变量,在这种情况下可以防止大量不必要的回溯,而无需任何明确的削减或额外的参数。
此外,不推荐使用 delete/3 以支持 select/3。但是,在这里您只需要 member/2 (因为您想重用元素)。
编辑
正如评论中所指出的,虽然 dif/2 使事情变得更短,但它可能有点令人困惑。这是一个几乎相同但略显冗长的版本,没有 dif/2(对于较大的 N,它恰好也工作得更快)。
p(N, Input, [First|Rest]) :-
N >= 1, N0 is N-1,
member(First, Input),
p(N0, Input, First, Rest).
p(0, _, _, []).
p(N, Input, Prev, [This|Rest]) :-
N > 0, N0 is N-1,
member(This, Input), This \= Prev,
p(N0, Input, This, Rest).