按位置在Prolog中对列表元素进行分组

时间:2016-10-28 16:39:58

标签: list sorting prolog grouping declarative

我找到了一个非常好的Erlang实现,用于按位置对列表成员进行分组。

everynth(List, N) ->
   [ lists:reverse(Y)
     || Y <- lists:foldl(
                     fun(X, [H|T]) -> T++[[X|H]] end,
                     lists:duplicate(N, []),
                     List)
   ].

这会将提供的列表(List)按其索引位置分为3组:

everynth([1, 2, 3, 4, 5, 6, 7, 8, 9], 3).

给出

[[1,4,7],[2,5,8],[3,6,9]].

我想在Prolog中做同样的事,但我不知道该怎么做。你能帮助我吗?谢谢。

3 个答案:

答案 0 :(得分:1)

直译

我们可以在Prolog中以与Erlang版本非常接近的方式实现它。

例如,使用Ulrich Neumerkel的library(lambda)

everynth(Ls, N, Groups) :-
        findall([], between(1,N,_), Groups0),
        foldl(\L^[G0|Gs0]^Gs^append(Gs0,[[L|G0]],Gs), Ls, Groups0, Groups1),
        maplist(reverse, Groups1, Groups).

根据您的Prolog系统,您可能需要导入一个或两个来运行它。

在其他相似之处中,这带来了一个非常不幸的缺点。 练习:哪个?提示:Imeanapartfromthename。

替代版本

为克服上述缺点,我还展示了替代解决方案。

构建块

我将使用的关键构建块是with_remainder_mod/5,定义为:

with_remainder_mod(N, L, R-L, I0, I) :-
        R #= I0 mod N,
        I #= I0 + 1.

从本质上讲,这为每个元素配备了 index (mod N)的残留。例如:

?- foldl(with_remainder_mod(3), [a,b,c,d,e,f], Rs, 0, _).
Rs = [0-a, 1-b, 2-c, 0-d, 1-e, 2-f].

我们几乎已完成,因为我们现在可以使用ISO标准谓词keysort/2,然后使用广泛可用的group_pairs_by_key/2或类似的库谓词:

?- foldl(with_remainder_mod(3), [a,b,c,d,e,f], Rs0, 0, _),
   keysort(Rs0, Rs),
   group_pairs_by_key(Rs, Gs).
Rs0 = [0-a, 1-b, 2-c, 0-d, 1-e, 2-f],
Rs = [0-a, 0-d, 1-b, 1-e, 2-c, 2-f],
Gs = [0-[a, d], 1-[b, e], 2-[c, f]].

完整解决方案

总的来说,完整的解决方案可能如下所示:

with_remainder_mod(N, L, R-L, I0, I) :-
        R #= I0 mod N,
        I #= I0 + 1.

every_nth(Ls0, N, Groups) :-
        foldl(with_remainder_mod(N), Ls0, Rs0, 0, _),
        keysort(Rs0, Rs),
        group_pairs_by_key(Rs, KeyGroups),
        pairs_values(KeyGroups, Groups).

时间复杂度为Θ(n·log n),其中 n 是列表的长度。

请注意,此解决方案使用:

  • 声明性整数算术通过CLP(FD)约束,使其在所有方向中可用。
  • foldl/5这是一个非常有用的,已经附带了多个Prolog系统。

同样,根据您的Prolog系统,您可能需要导入一个或两个以使用这些工具和其他谓词,如pairs_values/2或等效的。{/ p>

实施例

您的示例查询和答案:

?- every_nth([1,2,3,4,5,6,7,8,9], 3, Ls).
Ls = [[1, 4, 7], [2, 5, 8], [3, 6, 9]].

当然,更多一般性查询也是可能的:

?- length(Es, _), every_nth(Es, 3, Ls).
Es = Ls, Ls = [] ;
Es = [X1],
Ls = [[X1]] ;
Es = [X1, X2],
Ls = [[X1], [X2]] ;
Es = [X1, X2, X3],
Ls = [[X1], [X2], [X3]] ;
Es = [X1, X2, X3, X4],
Ls = [[X1, X4], [X2], [X3]] ;
Es = [X1, X2, X3, X4, X5],
Ls = [[X1, X4], [X2, X5], [X3]] ;
etc.

请注意,基于findall/3 的版本无法执行此操作,因为findall/3会创建新的副本变量。

以下是基准,可能对您在不同解决方案之间做出决定非常有用:

?- length(_, E),
   portray_clause(2^E),
   N #= 2^E, I #= N // 2,
   length(Ls, N),
   time(every_nth(Ls,I,_)),
   false.
2^0.
% 150 inferences, 0.000 CPU in 0.000 seconds (80% CPU, 4545455 Lips)
2^1.
% 34 inferences, 0.000 CPU in 0.000 seconds (78% CPU, 2428571 Lips)
2^2.
% 62 inferences, 0.000 CPU in 0.000 seconds (83% CPU, 4428571 Lips)
2^3.
% 118 inferences, 0.000 CPU in 0.000 seconds (86% CPU, 6555556 Lips)
2^4.
% 230 inferences, 0.000 CPU in 0.000 seconds (93% CPU, 8214286 Lips)
2^5.
% 454 inferences, 0.000 CPU in 0.000 seconds (94% CPU, 9659574 Lips)
2^6.
% 902 inferences, 0.000 CPU in 0.000 seconds (96% CPU, 10488372 Lips)
2^7.
% 1,798 inferences, 0.000 CPU in 0.000 seconds (92% CPU, 9364583 Lips)
2^8.
% 3,590 inferences, 0.000 CPU in 0.000 seconds (99% CPU, 10316092 Lips)
2^9.
% 7,174 inferences, 0.001 CPU in 0.001 seconds (99% CPU, 11297638 Lips)
2^10.
% 14,342 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 10491587 Lips)
etc.

答案 1 :(得分:0)

不是很优雅,但应该有用。

getEveryNth([], _, [], _).
getEveryNth([H|Ti], N, [H|To], N) :-
  getEveryNth(Ti, 1, To, N).
getEveryNth([_|Ti], M, Lo, N) :-
  M < N,
  Mp1 is M+1,
  getEveryNth(Ti, Mp1, Lo, N).

everyNthH(_, 0, [], _).
everyNthH([H|Ti], M, [[H|To] | Lo], N) :-
  M > 0,
  Mm1 is M-1,
  getEveryNth(Ti, 1, To, N),
  everyNthH(Ti, Mm1, Lo, N).

everyNth(L, N, LL) :-
  everyNthH(L, N, LL, N).

来自

everyNth([1, 2, 3, 4, 5, 6, 7, 8, 9], 3, L)

我得到(L统一)

[[1,4,7],[2,5,8],[3,6,9]]

答案 2 :(得分:0)

我不得不做一些尝试&amp;测试找到索引之间的正确关系,你可以从变量&#39; everynth_starting / 4的名称已经未列入&#39;轻松调试。

everynth(L, P, Gs) :-
    findall(G, (
        between(1, P, S),
        everynth_starting(L, P, S, G)
    ), Gs).
everynth_starting(L, P, S, G) :-
    findall(E, (nth1(I, L, E), S mod P=:=I mod P), G).

nth1 / 3充当对索引/元素

的生成器