Counting the same Prolog element
I want to count one item in a list and stop counting when another item appears and move on to the next same item.
The answers should be like this:
?- count(a,[a,a,a,a,b,a,a,a],X). X = [4,3] ?- count(a,[a,a,a,b,a,b,a,a,b,a,a,a,a],X). X = [3,1,2,4]
The code I wrote for count/3
:
count(_, [], []).
count(X, [X | T], N) :-
count(X, T, N1),
!,
N is N1 + 1.
count(X, [_ | T], N) :-
count(X, T, N).
I don't know how to get it to return a list of numbers. Can anyone help me? Thank.
source to share
The idea in my answer is to keep the list of run lengths open and add a new item to it when the run ends:
count(_, [], []).
count(Item, [Head|Tail], Counts) :-
count(Item, [Head|Tail], 0, Counts).
count(_, [], CurrentCount, [CurrentCount]).
count(Item, [Item|Tail], CurrentCount, Counts) :-
CurrentCountP1 is CurrentCount + 1,
count(Item, Tail, CurrentCountP1, Counts).
count(Item, [Head|Tail], CurrentCount, [CurrentCount|Counts]) :-
dif(Head, Item),
count(Item, Tail, 0, Counts).
?- count(a,[a,a,a,b,a,b,a,a,b,a,a,a,a], X). X = [3, 1, 2, 4] ; false.
source to share
This is how you can do it and save logical-purity!
In what follows, we use meta-predicates ( splitlistIfAdj/3
,
tfilter/3
and
maplist/3
) and equality / inequality predicates for terms ( (=)/3
and dif/3
).
Take E = a
and Xs0 = [a,a,a,b,a,b,a,a,b,a,a,a,a]
and create count/3
step by step:
-
First let it
Xs1
contain the runs of the elements inXs0
:? - Xs0 = [a, a, a, b, a, b, a, a, b, a, a, a, a], splitlistIfAdj (dif, Xs0, Xs1). Xs0 = [a, a, a, b, a, b, a, a, b, a, a, a, a], Xs1 = [[a, a, a], [b], [a], [b], [a, a], [b], [a, a, a, a]].
-
The run list
Xs1
contains all runs. Let themXs2
contain only those that interest us:? - Xs1 = [[a, a, a], [b], [a], [b], [a, a], [b], [a, a, a, a]], tfilter (\ [ X | _] ^ (X = a), Xs1, Xs2). Xs1 = [[a, a, a], [b], [a], [b], [a, a], [b], [a, a, a, a]], Xs2 = [[a, a, a], [a], [a, a], [a, a, a, a]].
-
Almost done! Finally, match
Xs2
(listE
-runs) with the corresponding run lengthsXs
:? - Xs2 = [[a, a, a], [a], [a, a], [a, a, a, a]], maplist (length, Xs2, Xs). Xs2 = [[a, a, a], [a], [a, a], [a, a, a, a]], Xs = [3, 1, 2, 4].
Now, put it all together!
count (E, Xs0, Xs): - splitlistIfAdj (dif, Xs0, Xs1), tfilter ( E + \ [X | _] ^ (X = E) , Xs1, Xs2),% works for _any_ item E maplist (length, Xs2, Xs).
Run multiple queries:
?- count(a,[a,a,a,a,b,a,a,a],Xs). Xs = [4,3]. % succeeds deterministically ?- count(a,[a,a,a,b,a,b,a,a,b,a,a,a,a],Xs). Xs = [3,1,2,4]. % succeeds deterministically
Since the code is monotonous , we also get logical answers for more general queries:
?- count(E,[a,a,a,b,a,b,a,a,b,a,a,a,a],Xs). Xs = [3,1,2,4], E = a ; Xs = [1,1,1], E = b ; Xs = [], dif(E,a), dif(E,b) .
source to share