简体   繁体   中英

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

The title is the required predicate and here are few sample queries

?- 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.

my code so far is this

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).

however it is not working properly

This compact fragment satisfies the queries you listed

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

but it has a big flaw (apart requiring library( yall )). Can you spot it?

Edit

About your code: you're doing it more complex than required, and ignoring the messages the compiler gives you about singletons.

Remember that accumulators reverse the list, so you should remove them. Build instead the list in the output argument.

To give you a start:

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,
    ....

fill the dots with the proper recursive calls. Then it will be working properly.

It helps to decompose your problem. You want to take a list of things and split it up into a individual sublists of N items, correct?

That is a matter of repeatedly doing the following:

  • Pulling no more than N items off the head of the list, and
  • Recursing down on what's left.

So, you need a predicated to pull no more than N items from the front of the list. There's 3 cases:

  • The general case: N > 0 and the list is non-empty. Here, we add the head of the list to the prefix that we're building, decrement N, and recurse down on what's left.

  • Special case #1: N > 0 and the list is empty. Here, we close the prefix and return the empty list as the suffix.

  • Special case #2: N is 0. Here, We close the prefix and return the source list as the suffix.

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  ) .

Once we have that, it's an even easier matter of repeatedly invoking first/4 .

  • If the source list is empty, then the list-of-lists is empty.
  • If the source list is non-empty, we
    • pull the first N items from the source list, and
    • recurse down on whatever's left over
split_every( _ , []     , []        ) .
split_every( N , [X|Xs] , [Pfx|LoL] ) :- first(N,[X|Xs],Pfx,Sfx), split_every(N,Sfx,LoL) .

You can, err... fiddle with it in this fiddle: https://swish.swi-prolog.org/p/split-list.pl

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM