Counting duplicate elements in prolog - prolog

i'm having problems with this subject in Prolog.
The thing is that I want to count the number of repeated elements appearing in a list,
and I also want to fill, in another list with 1, for each appearance of duplicated elements and a 0 if is not duplicated, e.g.
I have a list like this: [420,325,420,582,135,430,582], and the result should be [1,0,1,1,0,0,1].
I've tried some code snippets and it's driving me nuts.
The last code i've tried is:
count_duplicates([],[]).
count_duplicates([Head|Tail],[1|LS]):-
member(Head,Tail),
count_duplicates([Tail|Head],LS).
count_duplicates([Head|Tail],[0|LS]):-
\+ member(Head,Tail),
count_duplicates([Tail|Head],LS).
this predicate receive a list and have to generate the result list
Thanks in advance

You can try this :
count_duplicate(In, Out) :-
maplist(test(In), In, Out).
test(Src, Elem, 1) :-
select(Elem, Src, Result),
member(Elem, Result).
test(_Src, _Elem, 0).
EDIT Without maplist, you can do
count_duplicate(In, Out) :-
test(In, In, Out).
test(_, [], []).
test(In, [Elem | T], [R0 | R]) :-
select(Elem, In, Rest),
( member(Elem, Rest) -> R0 = 1; R0 = 0),
test(In, T, R).

I would rewrite using some of list processing builtins available:
count_duplicates(L, R) :-
maplist(check(L), L, R).
check(L, E, C) :-
aggregate(count, member(E, L), Occurs),
( Occurs > 1 -> C = 1 ; C = 0 ).
with that
?- count_duplicates([420,325,420,582,135,430,582],L).
L = [1, 0, 1, 1, 0, 0, 1].
About your code, I think it's simple to get termination:
count_duplicates([],[]).
count_duplicates([Head|Tail],[1|LS]):-
member(Head,Tail),
count_duplicates(Tail,LS).
count_duplicates([Head|Tail],[0|LS]):-
\+ member(Head,Tail),
count_duplicates(Tail,LS).
Note I corrected the recursive calls, and consider that could be done in a slightly more efficient way (both source and runtime) using the if .. then .. else .. construct.
count_duplicates([],[]).
count_duplicates([Head|Tail],[R|LS]):-
( member(Head,Tail) -> R = 1 ; R = 0 ),
count_duplicates(Tail,LS).
it's cleaner, isn't it? member/2 it's called just once, that's a big gain,
and consider using memberchk/2 instead of member/2.
But that code fails to tag as multiple the last occurrence.

Related

how can simulate nested loop in prolog?

how can i simulate this code in Prolog?
// L = an existing list ;
// function foo(var X, var Y)
result = new List();
for(int i=0;i<L.length;i++)
for(int j=0;j<L.length;j++){
result.add(foo(L.get(i), L.get(j));
}
nested loops are basically joins between sequences, and most of lists processing in Prolog is best expressed without indexing:
?- L=[a,b,c], findall(foo(X,Y), (member(X,L),member(Y,L)), R).
L = [a, b, c],
R = [foo(a, a), foo(a, b), foo(a, c), foo(b, a), foo(b, b), foo(b, c), foo(c, a), foo(c, b), foo(..., ...)].
edit
Sometime integers allow to capture the meaning in a simple way. As an example, my solution for one of the easier of Prolog context quizzes.
icecream(N) :-
loop(N, top(N)),
left, loop(N+1, center), nl,
loop(N+1, bottom(N)).
:- meta_predicate loop(+, 1).
loop(XH, PR) :-
H is XH,
forall(between(1, H, I), call(PR, I)).
top(N, I) :-
left, spc(N-I+1), pop,
( I > 1
-> pop,
spc(2*(I-2)),
pcl
; true
),
pcl, nl.
bottom(N, I) :-
left, spc(I-1), put(\), spc(2*(N-I+1)), put(/), nl.
center(_) :- put(/), put(\).
left :- spc(4).
pop :- put(0'().
pcl :- put(0')).
spc(Ex) :- V is Ex, forall(between(1, V, _), put(0' )).
Running in SWI-Prolog:
?- icecream(3).
()
(())
(( ))
/\/\/\/\
\ /
\ /
\ /
\/
true.
?- forall(loop(3,[X]>>loop(2,{X}/[Y]>>writeln(X-Y))),true).
1-1
1-2
2-1
2-2
3-1
3-2
true.
You can define a forto/4 meta-predicate easily. An example, taken from the Logtalk library loop object:
:- meta_predicate(forto(*, *, *, 0)).
forto(Count, FirstExp, LastExp, Goal) :-
First is FirstExp,
Last is LastExp,
forto_aux(Count, First, Last, 1, Goal).
:- meta_predicate(forto_aux(*, *, *, *, 0)).
forto_aux(Count, First, Last, Increment, Goal) :-
( First =< Last ->
\+ \+ (Count = First, call(Goal)),
Next is First + Increment,
forto_aux(Count, Next, Last, Increment, Goal)
; true
).
Example goal:
?- loop::forto(I, 1, 2, loop::forto(J, 1, 3, (write(I-J), nl))).
1-1
1-2
1-3
2-1
2-2
2-3
true.
Some Prolog compilers also provide built-in or library support for "logical loops" with good expressive power. Examples are (in alphabetic order) B-Prolog, ECLiPSe, and SICStus Prolog. Check the documentation of those systems for details. If you need a portable solution across most Prolog systems, check Logtalk's library documentation. Or simply take the above examples and define your own loop meta-predicates.
you can use this predicate using SICStus-prolog for looping variables I,J until N and get all of them inside fact foo/2 mentioned below successively ;
Code
loop(N) :- for(I,0,N),param(N) do
for(J,0,N),param(I) do
write(foo(I,J)),nl.
Result
| ?- loop(2).
foo(0,0)
foo(0,1)
foo(0,2)
foo(1,0)
foo(1,1)
foo(1,2)
foo(2,0)
foo(2,1)
foo(2,2)
yes

Python counter in Prolog

In Python you can do
>>> import from collections counter
>>> Counter(['a','b','b','c'])
>>> Counter({'b': 2, 'a': 1, 'c': 1})
Is there something similar in Prolog? Like so:
counter([a,b,b,c],S).
S=[a/1,b/2,c/1].
This is my implementation:
counter([],List,Counts,Counts).
counter([H|T],List,Counts0,[H/N|Counts]):-
findall(H, member(H,List), S),
length(S,N),
counter(T,List,Counts0,Counts).
counter(List,Counts):-
list_to_set(List,Set),
counter(Set,List,[],Counts).
It's rather verbose, so I wondered if there was a builtin predicate or a more terse implementation.
There is no builtin predicate, here is another way to do that :
counter([X], [X/1]).
counter([H | T], R) :-
counter(T, R1),
( select(H/V, R1, R2)
-> V1 is V+1,
R = [H/V1 | R2]
; R = [H/1 | R1]).
I like #joel76's solution. I will add a few more variations on the theme.
VARIATION I
Here's another simple approach, which sorts the list first:
counter(L, C) :-
msort(L, S), % Use 'msort' instead of 'sort' to preserve dups
counter(S, 1, C).
counter([X], A, [X-A]).
counter([X,X|T], A, C) :-
A1 is A + 1,
counter([X|T], A1, C).
counter([X,Y|T], A, [X-A|C]) :-
X \= Y,
counter([Y|T], 1, C).
Quick trial:
| ?- counter([a,b,b,c], S).
S = [a-1,b-2,c-1] ?
yes
This will fail on counter([], C). but you can simply include the clause counter([], []). if you want it to succeed. It doesn't maintain the initial order of appearance of the elements (it's unclear whether this is a requirement). This implementation is fairly efficient and is tail recursive, and it will work as long as the first argument is instantiated.
VARIATION II
This version will maintain order of appearance of elements, and it succeeds on counter([], []).. It's also tail recursive:
counter(L, C) :-
length(L, N),
counter(L, N, C).
counter([H|T], L, [H-C|CT]) :-
delete(T, H, T1), % Remove all the H's
length(T1, L1), % Length of list without the H's
C is L - L1, % Count is the difference in lengths
counter(T1, L1, CT). % Recursively do the sublist
counter([], _, []).
With some results:
| ?- counter([a,b,a,a,b,c], L).
L = [a-3,b-2,c-1]
yes
| ?- counter([], L).
L = []
yes
VARIATION III
This one uses a helper which isn't tail recursive, but it preserves the original order of elements, is fairly concise, and I think more efficient.
counter([X|T], [X-C|CT]) :-
remove_and_count(X, [X|T], C, L), % Remove and count X from the list
counter(L, CT). % Count remaining elements
counter([], []).
% Remove all (C) instances of X from L leaving R
remove_and_count(X, L, C, R) :-
select(X, L, L1), !, % Cut to prevent backtrack to other clause
remove_and_count(X, L1, C1, R),
C is C1 + 1.
remove_and_count(_, L, 0, L).
This implementation will work as long as the first argument to counter is instantiated.
SIDEBAR
In the above predicates, I used the Element-Count pattern rather than Element/Count since some Prolog interpreters, SWI in particular, offer a number of predicates that know how to operate on associative lists of Key-Value pairs (see SWI library(pairs) and ISO predicate keysort/2).
I also like #joel76 solution (and #mbratch suggestions, also). Here I'm just to note that library(aggregate), if available, has a count aggregate operation, that can be used with the ISO builtin setof/3:
counter(L, Cs) :-
setof(K-N, (member(K, L), aggregate(count, member(K, L), N)), Cs).
yields
?- counter([a,b,b,c], L).
L = [a-1, b-2, c-1].
If the selection operation was more complex, a nice way to avoid textually repeating the code could be
counter(L, Cs) :-
P = member(K, L),
setof(K-N, (P, aggregate(count, P, N)), Cs).
edit
Since I'm assuming library(aggregate) available, could be better to task it the set construction also:
counter(L, Cs) :-
P = member(E,L), aggregate(set(E-C), (P, aggregate(count,P,C)), Cs).

Prolog: Create list containing elements at even indices

Basically, I need to write a predicate, even_elts(L,M), such that L is a new list generated that contains only the even indexed elements from M (0th, 2nd, 4th, etc)
add_tail([X],[],X).
add_tail([H|NewT],[H|T],X) :-
add_tail(NewT,T,X).
even_elts(L,[]) :- L = [].
even_elts(L,M) :- even_elts2(L,M,1).
even_elts2(L,[H2|T2],Ct) :-
Ct2 is Ct + 1,
((Ct2 mod 2) =:= 0, add_tail(L,L2,H2), even_elts2(L2,T2,Ct2); even_elts2(L,T2,Ct2)).
even_elts2(_,[],_) :- !.
This works if M is empty or contains 1 or 2 elements. But, it only gets the first even indexed element from M, not the rest. Any pointers
EDIT: Solved the problem a different way, by removing the odd indexed elements rather than trying to create a new list and copying the data over. But, if someone can figure out a solution for my original code, I would be interested to see.
You're making this much more complicated than it is. You can use pattern matching to get each even element out, then collect those in the second (output) argument.
% an empty list does not have even elements
even_elts([], []).
% for all other lists, skip the second element (_),
% add the first to the output, recurse
even_elts([X, _ | L], [X | R]) :-
even_elts(L, R).
Just another approach with accumulator:
even_elts(L,M) :-
even_elts(M,0,[],L).
even_elts([H|T],I,Acc,Ans) :-
( I mod 2 =:= 0, append(Acc,[H], AccNew)
; I mod 2 =:= 1, AccNew = Acc
),
Inew is I + 1,
even_elts(T,Inew,AccNew,Ans).
even_elts([],_,Acc,Acc).
And
?- even_elts(X,[1,2,3,4,5]).
X = [1, 3, 5] ;
evens([A,B|C], [A|D]):- !, .....
evens(X, X).
is all you need. Fill in the blanks. :)

Prolog programs - how to make it work?

I have these two programs and they're not working as they should. The first without_doubles_2(Xs, Ys)is supposed to show that it is true if Ys is the list of the elements appearing in Xs without duplication. The elements in Ys are in the reversed order of Xs with the first duplicate values being kept. Such as, without_doubles_2([1,2,3,4,5,6,4,4],X) prints X=[6,5,4,3,2,1] yet, it prints false.
without_doubles_2([],[]).
without_doubles_2([H|T],[H|Y]):- member(H,T),!,
delete(H,T,T1),
without_doubles_2(T1,Y).
without_doubles_2([H|T],[H|Y]):- without_doubles_2(T,Y).
reverse([],[]).
reverse([H|T],Y):- reverse(T,T1), addtoend(H,T1,Y).
addtoend(H,[],[H]).
addtoend(X,[H|T],[H|T1]):-addtoend(X,T,T1).
without_doubles_21(X,Z):- without_doubles_2(X,Y),
reverse(Y,Z).
The second one is how do I make this program use a string? It's supposed to delete the vowels from a string and print only the consonants.
deleteV([H|T],R):-member(H,[a,e,i,o,u]),deleteV(T,R),!.
deleteV([H|T],[H|R]):-deleteV(T,R),!.
deleteV([],[]).
Your call to delete always fails because you have the order of arguments wrong:
delete(+List1, #Elem, -List2)
So instead of
delete(H, T, T1)
You want
delete(T, H, T1)
Finding an error like this is simple using the trace functionality of the swi-prolog interpreter - just enter trace. to begin trace mode, enter the predicate, and see what the interpreter is doing. In this case you would have seen that the fail comes from the delete statement. The documentation related to tracing can be found here.
Also note that you can rewrite the predicate omitting the member check and thus the third clause, because delete([1,2,3],9001,[1,2,3]) evaluates to true - if the element is not in the list the result is the same as the input. So your predicate could look like this (name shortened due to lazyness):
nodubs([], []).
nodubs([H|T], [H|Y]) :- delete(T, H, T1), nodubs(T1, Y).
For your second question, you can turn a string into a list of characters (represented as ascii codes) using the string_to_list predicate.
As for the predicate deleting vovels from the string, I would implement it like this (there's probably better solutions for this problem or some built-ins you could use but my prolog is somewhat rusty):
%deleteall(+L, +Elems, -R)
%a helper predicate for deleting all items in Elems from L
deleteall(L, [], L).
deleteall(L, [H|T], R) :- delete(L, H, L1), deleteall(L1, T, R).
deleteV(S, R) :-
string_to_list(S, L), %create list L from input string
string_to_list("aeiou", A), %create a list of all vovels
deleteall(L, A, RL), %use deleteall to delete all vovels from L
string_to_list(R, RL). %turn the result back into a string
deleteV/2 could make use of library(lists):
?- subtract("carlo","aeiou",L), format('~s',[L]).
crl
L = [99, 114, 108].
while to remove duplicates we could take advantage from sort/2 and select/3:
nodup(L, N) :-
sort(L, S),
nodup(L, S, N).
nodup([], _S, []).
nodup([X|Xs], S, N) :-
( select(X, S, R) -> N = [X|Ys] ; N = Ys, R = S ),
nodup(Xs, R, Ys).
test:
?- nodup([1,2,3,4,4,4,5,2,7],L).
L = [1, 2, 3, 4, 5, 7].
edit much better, from ssBarBee
?- setof(X,member(X,[1,2,2,5,3,2]),L).
L = [1, 2, 3, 5].

Can't get minimize from CLPFD to work

Me and a friend are writing a program which is supposed to solve a CLP problem. We want to use minimize to optimize the solution but it won't work, because it keeps saying that the number we get from sum(P,#=,S) is between two numbers (for example 5..7). We haven't been able to find a good way to extract any number from this or manipulate it in any way and are therefore looking for your help.
The problem seems to arise from our gen_var method which says that each element of a list must be between 0 and 1, so some numbers come out as "0..1" instead of being set properly.
Is there any way to use minimize even though we get a number like "5..7" or any way to manipulate that number so that we only get 5? S (the sum of the elements in a list) is what we're trying to minimize.
gen_var(0, []).
gen_var(N, [X|Xs]) :-
N > 0,
M is N-1,
gen_var(M, Xs),
domain([X],0,1).
find([],_).
find([H|T],P):- match(H,P),find(T,P).
match(pri(_,L),P):-member(X,L), nth1(X,P,1).
main(N,L,P,S) :- gen_var(N,P), minimize(findsum(L,P,S),S).
findsum(L,P,S):- find(L,P), sum(P,#=,S).
I've slightly modified your code, to adapt to SWI-Prolog CLP(FD), and it seems to work (kind of). But I think the minimum it's always 0!
:- use_module(library(clpfd)).
gen_var(0, []).
gen_var(N, [X|Xs]) :-
N > 0,
M is N-1,
gen_var(M, Xs),
X in 0..1 .
find([], _).
find([H|T], P):-
match(H, P),
find(T, P).
match(pri(_,L),P):-
member(X, L),
nth1(X, P, 1).
findsum(L,P,S) :-
find(L, P),
sum(P, #=, S).
main(N, L, P, S) :-
gen_var(N, P),
findsum(L, P, S),
labeling([min(S)], P).
Is this output sample a correct subset of the expected outcome?
?- main(3,A,B,C).
A = [],
B = [0, 0, 0],
C = 0 ;
A = [],
B = [0, 0, 1],
C = 1 ;

Resources