4

I'd like to produce a predicate which is able to check whether a list will divide into sublists each containing at least three atoms.

For example:

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

Has solutions such as:

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

However, the following will fail:

findgroups([1,2,3,4,5]).

I have written a few predicates based on splitting lists into two sub terms using append, but I'm confused about handling multiple sublists.

Thank you very much for your help

With Best wishes J

false
  • 10,264
  • 13
  • 101
  • 209
John
  • 303
  • 2
  • 12

2 Answers2

4

Let's start with a good name! findgroups/2 that means you want to command Prolog: Prolog do find me some groups! And hurry up! Is this really fit? Let's see:

?- findgroups(L, [[1,2,3],[4,5,6],[7,8,9]]).

Who is finding here any group? They are already present! So your name is really a misnomer. Instead, compose a name by describing each argument. The first argument is about a list, the second about a list of lists. So:

?- list_lists(L, [[1,2,3],[4,5,6],[7,8,9]]).

Better, but a bit too general. Maybe:

?- elements_trigroups(L, [[1,2,3],[4,5,6],[7,8,9]]).

So the query now reads: Here are all the trigroups, what are the corresponding elements? Elements is such a lengthy word, els is just as nice.

My first (and a little bit too general) attempt:

es_trigroups(Es, Ess) :-
   phrase(seqq(Ess), Es).

Note the variable names: Es: a list of Es. And Ess is a list of Ess which in turn is a list of Es.

Using seqq//1 defined in another answer.

Darn, it still succeeds for:

?- es_trigroups([1,2,3,4,5], [Es,Fs]).
   Es = [], Fs = [1,2,3,4,5]
;  Es = [1], Fs = [2,3,4,5]
;  ... .

But it is not that bad, for it succeeds for all the cases you demanded. Brief: The relation is too general. We need to specialize it.

A simple way to specialize a relation is to add further goals like:

es_trigroups(Es, Ess) :-
   trigroups(Ess),
   phrase(seqq(Ess), Es).

trigroups([]).
trigroups([Es|Ess]) :-
   Es = [_,_,_|_],
   trigroups(Ess).

So now everything seems fine. There is only one thing, that is not that nice: The termination property could be better. Sometimes we will need to exchange the goals.

es_trigroups_bis(Es, Ess) :-
   phrase(seqq(Ess), Es),
   trigroups(Ess).

The resulting termination conditions read:

es_trigroups(A,B)terminates_if b(B).
    % optimal. loops found: [es_trigroups(_,[[_,_,_|_]|_]),es_trigroups(y,[[_,_,_|_]|_])]. NTI took    0ms,74i,74i
es_trigroups_bis(A,B)terminates_if b(B).
    % optimal. loops found: [es_trigroups_bis(_,[[]|_]),es_trigroups_bis(y,[[]|_])]. NTI took    0ms,79i,79i

But what we actually want, is that the termination condition not only depends on B (the second argument), but also on A alone. That is that either A or B alone can guarantee termination. To this end, we have to interlace both definitions:

es_trigroups_ter(Es, Ess) :-
   phrase(seq3s(Ess), Es).

seq3s([]) --> [].
seq3s([Es|Ess]) --> {Es = [_,_,_|_]}, seq(Es), seq3s(Ess).

Now, termination conditions are excellent!

es_trigroups_ter(A,B)terminates_if b(A);b(B).
    % optimal. loops found: [es_trigroups_ter([A|_],[[A,_,_|_]|_])]. NTI took    0ms,77i,77i
false
  • 10,264
  • 13
  • 101
  • 209
0

Maybe this very simple solution could do

findgroups([], []).
findgroups(L, [[A,B,C|D]|Gs]) :-
    append([A,B,C|D], R, L),
    findgroups(R, Gs).

The last one solution is not requested, but it's correct, since you don't rule out a group of only 1 element

?- findgroups([1,2,3,4,5,6,7,8,9], Gs).
Gs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] ;
Gs = [[1, 2, 3], [4, 5, 6, 7, 8, 9]] ;
Gs = [[1, 2, 3, 4], [5, 6, 7, 8, 9]] ;
Gs = [[1, 2, 3, 4, 5], [6, 7, 8, 9]] ;
Gs = [[1, 2, 3, 4, 5, 6], [7, 8, 9]] ;
Gs = [[1, 2, 3, 4, 5, 6, 7, 8|...]] ;
false.

To match exactly the examples, I would write

findgroups(L, Gs) :- findgroups_(L, Gs), Gs = [_,_|_].
findgroups_([], []).
findgroups_(L, [[A,B,C|D]|Gs]) :-
    append([A,B,C|D], R, L),
    findgroups_(R, Gs).
CapelliC
  • 59,646
  • 5
  • 47
  • 90
  • Thanks both for your excellent responses, which helped me learn a lot. I am currently experimenting with adding additional goals to constrain the grouping further. – John Apr 24 '16 at 18:48