I have a question in a course assignment.
Consider repetitive lists of the form
[a,a,a,b,b,c,a,a,a,a]
and their compact form, defined as lists of couples,[[a,3],[b,2],[c,1],[a,4]]
.Define the predicate
compress/2
such thatcompress(+L1, ?L2)
is satisfied if, given a listL1
,L2
is its compact form.
So far, I have come up with the code below:
compress(X,[[X,1]]).
compress([H1,H2|T1],[[H1,C]|T2]):-
H1 = H2,
compress(T1,T2),
C is R + 1.
I am not sure if I am doing it right. Could someone please point to the right direction.
Here are some ideas to get you started.
You're going to need to keep a running count of repeated elements since your results have counters. So right off, consider an auxiliary predicate that includes the counter, which is a typical way of handling it in Prolog. This use of a counter is commonly referred to as an accumulator.
Now you'll need to consider a few different cases:
This says that if I compress an empty list, I get an empty list.
This one says if I compress a list of one element and my current running count is
Count
, then the result is[[H, Count]]
.This is the case where I have a running count and the element is still repeating. The result is going to be a list
TC
but I don't know what it looks like yet since we're still in a repeating cycle and it will need to be determined through recursion. What should this predicate look like? In your example, you included a count in the result when the first two elements were the same, which is not the right time to include the count (see the clause below).This is the case where I have a running count and the repeating stops at
H1
. Since the repeating of the current cycle ends withH1
, we know the result looks like[[H1, Count]|TC]
becauseH1
has repeatedCount
times. We just have yet to determine the rest of the listTC
through recursion. What should this predicate implementation look like?There are other ways of doing the above logic (e.g., with
->
and;
construct, etc), but this will keep it simple.Try to think of these as rules where the head of the predicate clause is the assertion which will be true if the following elements of the clause are true. And think recursively.
As an afterthought, this could be done without a separate accumulator by using the result to carry the accumulator:
Here's how you could do it using
splitlistIfAdj/3
in combination withdif/3
.First, determine the runs of equal adjacent list items:
Then, map each run to its length using
maplist/3
andlength/2
:Almost done! Let's put it all together using Prolog lambdas:
Sample query:
I chose to do it this way:
The
enumerate
predicate simply maps[a, a, a, b, b, c, a, a, a, a]
to[[a, 1], [a, 1], [a, 1], [b, 1], [b, 1], [c, 1], [a, 1], [a, 1], [a, 1], [a, 1]]
.Then I
collapse
this list down by matching the first two heads of the list - if they unify add the values and try tocollapse
again. If they don't unify then pop one element off of the list andcollapse
again. Otherwise there are two base cases - an empty list and a list with one element.The result is:
[[a, 3], [b, 2], [c, 1], [a, 4]]
.