Finding number of unique elements in the list - prolog

I want to write a code which will return number of unique elements in the list. My idea is to check if head of the list is there in the list of uniqeue elemeents. If it is not, add it to the list and increment the counter and continue witht the remaining list. So, I tried out following:
count([],0).
count([X,T], N) :-
count([X,T], [], N). %initially list of unique elements is empty
count([X,T], U, N) :- % U is a list of unique elements
(not(member(X, U)), append(U, X, U2));
count(T, U2, N1),
N is N1 + 1.
%------ helpers ------
member_(X,[Y|T]):-
member_(X, T).
member_(X,[X|_]).
append([], Y, [Y]). % append[] and Y to get Y.
append([H|X], Y, [H|Z]) :- append(X, Y, Z). % append [H|X] and Y to get [H|Z] if appending X and Y gives Z
But running above simply return false:
3 ?- count([1,2,3],N).
Call: (10) count([1, 2, 3], _7714) ? creep
Fail: (10) count([1, 2, 3], _7714) ? creep
false.
Why is this so?

But running above simply return false:
3 ?- count([1,2,3],N).
Call: (10) count([1, 2, 3], _7714) ? creep
Fail: (10) count([1, 2, 3], _7714) ? creep
false.
Why is this so?
Because there is no head of a clause that count([1,2,3],N) can match.
You have two clauses for count/2:
count([],0). %%% clause 1
count([X,T], N) :- ... %%% clause 2
Since [1,2,3] can't be unified with [], clause 1 is no match.
But [1,2,3] can also not be unified with [X,T], thus clause 2 is also no match. Therefore, count([1,2,3],N) cannot be proven.
You can test this by asking
?- [X,T]=[1,2,3].
false.
What you need to recursively break down lists of arbitrary length is [X|T] instead. (Please test ?- [X|T]=[1,2,3]. to see how you could fix your code.)

Related

Useless "no" errors when writing Prolog sorting queries

I tried to write a Prolog query that would sort a given list. Tried two different attempts so far, both attempting to implement insertion sort, but I am unsure why each one failed. Currently using GNU Prolog version 1.5.0 (64 Bits).
Attempt 1: (tried to the insertion and the sorting both in the same query. Very imperative)
% Use: sortz([1,3,2], What). or sortz([1,3,2], [], What).
% Attempt to use insertion sort, not sure what's wrong.
% Result will be the last parameter when first element is empty, thus all elements will have been sorted into the second list
sortz([], Sorted, Sorted).
% If there is only 1 value, it is sorted
sortz([Head|Tail], [], Result) :- sortz(Tail, Head, Result).
% Upon encountering the first element smaller or equal to Head, insert Head at that location
sortz([Head|Tail], [SortedHead|SortedTail], Result) :- Head =< SortedHead, sortz(Tail, [Head|[SortedHead|SortedTail]], Result).
% If Head is bigger than current SortedHead, get the result of sorting Head with SortedTail, then continue sorting
sortz([Head|Tail], [SortedHead|SortedTail], Result) :- Head > SortedHead, sortz([Head], SortedTail, SortedRest), sortz(Tail, [SortedHead|SortedRest], Result).
% Shortcut
sortz(List, Result) :- sortz(List, [], Result).
Running sortz([1,3,2], What). and sortz([1,3,2], [], What). both return "no".
Attempt 2:
% Shortcut
insertionsort(List, Result) :- insertionsort(List, [], Result).
% Base case. Result will be the last parameter when first element is empty, thus all elements will have been sorted into the second list
insertionsort([], Result, Result).
insertionsort([Head|Tail], Acc, Result) :- insert(Head, Acc, Inter), insertionsort(Tail, Inter, Result).
% Base case
insert([], Result, Result).
% If X is smaller or equal to Head, insert X before Head
insert(X, [Head|Tail], Inter) :- X =< Head, insert([], [X|[Head|Tail]], Inter).
% If X is greater, insert X into the Tail, then append the sorted list onto head
insert(X, [Head|Tail], Inter) :- X > Head, insert(X, Tail, Sortedrest), insert([], [Head|Sortedrest], Inter).
% Base case
insert(X, [], [X]).
Running insertionsort([1,3,2], What). and insertionsort([1,3,2], [], What). both return "What = [1,2,3] ?" first, but upon entering ";", I get "uncaught exception: error(type_error(evaluable,[]/0),(=<)/2)".
Would appreciate any insight as to why these two code samples are wrong. I have already found working examples of sorts elsewhere, but I'm just stuck on why Attempt 1 in particular isn't working. Probably missing a case or two somewhere but I'm not sure how to debug Prolog's "no"s...
Looking at attempt 1 (using swi-prolog):
?- trace, sortz([2,1,3], S).
Call: (11) sortz([2, 1, 3], _13888) ? creep
Unify: (11) sortz([2, 1, 3], _13888)
Call: (12) sortz([2, 1, 3], [], _13888) ? creep
Unify: (12) sortz([2, 1, 3], [], _13888)
Call: (13) sortz([1, 3], 2, _13888) ? creep
Fail: (13) sortz([1, 3], 2, _13888) ? creep
That is wrong, because the 2nd argument should be a list, i.e. [2] instead of 2.
Replace the one line to be:
sortz([Head|Tail], [], Result) :- sortz(Tail, [Head], Result).
... and then it works.

Get index of given element in list

I am trying to solve some basic puzzles to learn prolog. I am trying to get the index of a given element in a list with recursion. I got stuck trying to solve the problem and I am not sure why. When I execute this it only returns "false" instead of the index.
elem(_, [], 0).
elem(E, [E | T], RES) :-
elem(E, T, CUR_RES), RES is CUR_RES + 1.
An example query I use to check the code elem(2, [1, 2], X).
Your problem with
elem(2, [1, 2], X).
is that when it tries to unify with the base case
elem(_, [], 0).
it fails because the second parameter is not empty.
When it tries to unify with the recursive case
elem(E, [E | T], RES) :-
elem(E, T, CUR_RES),
RES is CUR_RES + 1.
E is 2, which requires the list to be [2|T] but since the list is [1,2] it also can not unify.
This is closer to what you want
elem(E,[E|_],0).
elem(E,[_|T],Res) :-
elem(E,T,Cur_rest),
Res is Cur_rest + 1.
Example runs:
?- elem(2, [1, 2], X).
X = 1 ;
false.
?- elem(1, [1, 2], X).
X = 0 ;
false.
?- elem(4, [1,2,3,4,5], X).
X = 3 ;
false.
You need to do the matching for the value you are seeking in the base case and not the recursive case. Once you do that the rest of changes follow as needed.

Prolog unifies a variable with a term then forgets the unification

I have the following predicate which I have written for recognising when two lists are the same except the two elements at indices I1 and I2 are swapped:
swapped(I1, I2, List, NewList) :-
% The lists are the same length and the two indices are swapped.
same_length(List, NewList),
nth0(I1, List, V1), nth0(I2, List, V2),
nth0(I1, NewList, V2), nth0(I2, NewList, V1),
% All the other indices remain the same.
proper_length(List, Length), Lim is Length - 1,
numlist(0, Lim, Indices),
forall((member(I, Indices), I \= I1, I \= I2),
(nth0(I, List, V), nth0(I, NewList, V))).
The following swipl output demonstrates my issue:
?- swapped(0, 1, [1,2,3], L).
L = [2, 1, _G5035].
?- swapped(0, 1, [1,2,3], [2,1,3]).
true.
?- swapped(0, 1, [1,2,3], [2,1,4]).
false.
Why does it return a variable for the third element rather than just 3, given that it can recognise that 3 is the only correct term? These are the last four parts of the trace where the unification happens and is then forgotten:
Call: (10) lists:nth0(2, [2, 1, _G6121], 3) ? creep
Exit: (10) lists:nth0(2, [2, 1, 3], 3) ? creep
^ Exit: (8) forall(user: (member(_G6145, [0, 1, 2]), _G6145\=0, _G6145\=1), user: (nth0(_G6145, [1, 2, 3], _G6162), nth0(_G6145, [2, 1, _G6121], _G6162))) ? creep
Exit: (7) swapped(0, 1, [1, 2, 3], [2, 1, _G6121]) ? creep
I don't doubt there's a better way to swap two elements (perhaps recursively) but I would like to know why this is happening and how to fix it; I'm clearly lacking in some Prolog knowledge.
Thanks!
forall/2 is a so called 'failure driven loop'. Then instantiations are undone between cycles.
In SWI-Prolog, there is foreach/2, that fixes the problem with your first query.
...
numlist(0, Lim, Indices),
foreach((member(I, Indices), I \= I1, I \= I2),
(nth0(I, List, V), nth0(I, NewList, V))).
Test:
?- swapped(0, 1, [1,2,3], L).
L = [2, 1, 3].
In SWI-Prolog, sometime the better way to understand a builtin is to inspect the source. You can see that foreach/2 is a fairly complicated predicate... from swipl prompt, try ?- edit(foreach)., or follow the source link from the doc page (the circled :-).

Prolog predicate check divisibility of numbers in a list

example
divisible([L1],X) :-
L1 mod X =:= 0.
query
divisible([4,6,8,7],2).
response
[4,6,8]
Any guidance?
divisible([], _, []).
divisible([H|T], X, [H|T1]) :- H mod X =:= 0, divisible(T, X, T1).
divisible([H|T], X, T1) :- H mod X =\= 0, divisible(T, X, T1).
You are going to need a three-argument predicate (input list, value to test for divisibility, and output list). After that, think about the three cases: input list is empty, first element is not divisible by number, and first element is divisible by number. You should be able to write three clauses, one for each of those, and get a correct predicate.
SWI-Prolog has a nice predicate include/3 which you can use like this:
?- include(divides(2), [4, 6, 8, 7], L).
L = [4, 6, 8].
given that you have defined divides/2:
% Succeeds if X divides Y
divides(X, Y) :-
Y mod X =:= 0.
Use meta-predicate tfilter/3 in tandem with the reified test predicate divisor_of_t/3:
?- tfilter(divisor_of_t(2),[4,6,8,7],Zs).
Zs = [4, 6, 8].
Based on clpfd and bool01_truth/2, we can define divisor_of_t/3 as follows:
:- use_module(library(clpfd)).
divisor_of_t(Y,X,Truth) :- X mod Y #= 0 #<==> B, bool01_truth(B,Truth).

prolog program for determining whether any two pairs in a list have the same sum

How can I write a relation in prolog that determines if there are any two pairs in a list with the same sum. The relation should fail if there exist no pairs whose sums are equal. The relation should also fail if the list contains less than four elements.
list([1 2 3]) fails since it only has 3 elements
list([2 3 4 1]) succeeds since 2+3=4+1
list([3 1 2 4 5 6]) succeds since 5+1=2+4
list([1 8 20 100]) fails since there are no pairs with equal sums
How about this algorithm: take any two pairs of numbers, and see if they match. Here is the code for it:
has_equal_sums(List) :-
select(A, List, List2),
select(B, List2, List3),
select(C, List3, List4),
select(D, List4, _),
A+B =:= C+D.
If you want to make sure it works, or for debug purpose, you can display the two selected pairs as an output:
has_equal_sums(List, [[A, B], [C, D]]) :-
select(A, List, List2),
select(B, List2, List3),
select(C, List3, List4),
select(D, List4, _),
A+B =:= C+D.
Here are a few examples of usage:
?- has_equal_sums([1, 2, 3, 6, 5], X).
X = [[1,6],[2,5]] ? ;
X = [[2,6],[3,5]] ?
?- has_equal_sums([1, 2, 3, 5], X).
no
?- has_equal_sums([1, 2, 3], X).
no
So I checked with my professor and since our deadline has passed, he is OK with me posting my solution to this problem. This is probably not the most succinct way to solve the problem, and I'm leaning on my Scheme a bit, but it appears to work:
%car operations
car([],null).
car([X|_],X).
cadr([_|L],R) :-
car(L,R).
caddr([_|L],R) :-
cadr(L,R).
%cdr operations
cdr([],[]).
cdr([_|L],L).
cddr([_|L],R) :-
cdr(L,R).
cdddr([_|L],R) :-
cddr(L,R).
%two-pair operation
% This algorithm is based on the provided example
% solution for CSC388FA09HW4.
long-enough(L,_) :-
length(L,X),
X>3.
too-long(L,_) :-
length(L,X),
X>4.
two-pair([Head|Tail]) :-
long-enough([Head|Tail],_),
(
(car(Tail,N2),cadr(Tail,N3),caddr(Tail,N4),Head+N2=:=N3+N4);
(cadr(Tail,N2),car(Tail,N3),caddr(Tail,N4),Head+N2=:=N3+N4);
(caddr(Tail,N2),car(Tail,N3),cadr(Tail,N4),Head+N2=:=N3+N4)
);
too-long([Head|Tail],_),
(
two-pair(Tail);
cdr(Tail,N2),two-pair([Head|N2]);
car(Tail,N2),cddr(Tail,N3),two-pair([Head|[N2|N3]]);
car(Tail,N2),cadr(Tail,N3),cdddr(Tail,N4),two-pair([Head|[N2|[N3|N4]]])).

Resources