一个序言谓词,用于将列表拆分为单独的列表,每 n 个元素

A prolog predicate to split a list into sperate lists ever n elements

标题是必需的谓词,这里有几个示例查询

?- splitEvery(2,[a,b,c,d,e,f,g,h],R).

R = [[a, b], [c, d], [e, f], [g, h]] ;

false.

?- splitEvery(4,[a,b,c,d,e,f,g,h],R).

R = [[a, b, c, d], [e, f, g, h]] ;

false.

?- splitEvery(8,[a,b,c,d,e,f,g,h],R).

R = [[a, b, c, d, e, f, g, h]] ;

false.

到目前为止我的代码是这样的

splitEvery(N,List,Res):-

    splitEveryHelper1(N,List,Res,1,[]).

splitEveryHelper1(_,[],Acc,_,Acc).

splitEveryHelper1(N,[H|T],Res,C,[[H|HT]|AT]):-

    C=<N,

    C\_new is C+1,

    splitEveryHelper1(N,T,Res,C_new,[[HT]|AT]).

splitEveryHelper1(N,List,Res,C,[AH|TR]):-

    C>N,

    C\_new=1,

    splitEveryHelper1(N,List,Res,C_new,AT).

但是它不能正常工作

此压缩片段满足您列出的查询

splitEvery(N,L,R) :-
    length(R,_),
    maplist({N}/[X]>>length(X,N),R),
    append(R,L).

但它有一个很大的缺陷(除了需要库(yall))。你能发现吗?

编辑

关于你的代码:你做的比要求的更复杂,并且忽略了编译器给你的关于单例的消息。

请记住,累加器会反转列表,因此您应该删除它们。在输出参数中构建列表。

给你一个开始:

splitEvery(N,List,Res):-
    splitEveryHelper1(N,List,1,Res).

splitEveryHelper1(_,[],_,[]).
splitEveryHelper1(N,[H|T],C,[[H|R]|AT]):-
    C=<N,
    C_new is C+1,
    ....
splitEveryHelper1(N,List,C,[[]|TR]):-
    C>N,
    C_new=1,
    ....

用适当的递归调用填满圆点。然后它会正常工作。

有助于分解你的问题。您想获取事物列表并将其拆分为 N 项的单独子列表,对吗?

这是重复执行以下操作的问题:

  • 从列表头部拉出不超过 N 项,并且
  • 对剩下的内容进行递归。

因此,您需要一个谓词来从列表的前面提取不超过 N 项。有 3 种情况:

  • 一般情况:N > 0 且列表非空。在这里,我们将列表的头部添加到我们正在构建的前缀中,递减 N,然后递归剩下的内容。

  • 特例 #1:N > 0 且列表为空。在这里,我们关闭前缀和 return 空列表作为后缀。

  • 特殊情况 #2:N 为 0。这里,我们关闭前缀和 return 源列表作为后缀。

first( N , [X|L] , [X|Xs] , Sfx ) :- N > 0 , N1 is N-1 , first( N1, L, Xs, Sfx ) .
first( N , []    , []     , []  ) :- N > 0 .
first( 0 , Xs    , []     , Xs  ) .

一旦我们有了它,重复调用 first/4.

就更容易了
  • 如果源列表为空,则列表列表为空。
  • 如果源列表非空,我们
    • 从源列表中拉出前 N 项,然后
    • 对剩下的东西进行递归
split_every( _ , []     , []        ) .
split_every( N , [X|Xs] , [Pfx|LoL] ) :- first(N,[X|Xs],Pfx,Sfx), split_every(N,Sfx,LoL) .

你可以,呃……fiddle 在这个 fiddle: https://swish.swi-prolog.org/p/split-list.pl