Prolog - How to calculate the total number of elements - prolog

How do you calculate the total number of elements in a list when there can be lists within a list? (add the length of all the lists together)
?- sumOfLists([[1,2],[],[a,s,d],[a,1,3],[1]],S).
S = 9

Here is the shorter way I know, using flatten/2:
sumOfLists(Ls, S) :-
flatten(Ls, Flat),
length(Flat, S).

You can do this without flattening and without using maplist.
Here is my proposal:
%element atomic? -> count
deep_len([H|T],N) :- atomic(H), deep_len(T, M), N is M + 1.
%element list? -> recurse for head and tail and sum results
deep_len([H|T],N) :- is_list(H), deep_len(H, Sum1), deep_len(T, Sum2), N is Sum1+Sum2.

First, use maplist to replace each list by it's length, then sum up the lengths.
Using a sum predicate like this one:
sum(L, S) :- sum(L, S, 0).
sum([], Acc, Acc).
sum([X|Xs], Sum, Acc) :-
Acc1 is Acc + X,
sum(Xs, Sum, Acc1).
it boils down to just:
sumOfLists(L, S) :- maplist(length, L, Lengths), sum(Lengths, S).

Related

Prolog - transforming to tail recursion

As a newbie in Prolog, I learned that tail recursion is optimized. Therefore, I am trying to convert the following program to tail-recursive ones.
sum([], 0).
sum([H|T], N):-
sum(T, X),
N is X + H.
Here is what I've tried, and it's obvious that I missed something in my logics:
sum(List,Sum):-
sum1(List,0,Sum).
sum1([Element|List],Accumulator,Sum):-
(NewAccumulator is Accumulator + Element,
sum1(List,NewAccumulator,Sum);
List=[] -> Sum = Accumulator
).
The problem in my program is adding all the numbers except the last one in the list. How can I improve this program? Thanks.
The problem is that you wrote the program wrongly. This would be correct:
sum(List, Sum) :-
sum_1(List, 0, Sum).
sum_1([], Sum, Sum).
sum_1([H|T], Sum0, Sum) :-
Sum1 is Sum0 + H,
sum_1(T, Sum1, Sum).
but you could have googled some version of this here on stackoverflow.
This is also trivially a fold on a list:
sum([H|T], Sum) :-
foldl(add, T, H, Sum).
add(X, Y, Z) :- Z is X + Y.
I agree with TA_intern's solution, but here is an addition to show you why your program did go wrong.
The following program leaves as much of your code as possible, but is correct:
sum(List,Sum):-
sum1(List,0,Sum).
sum1([Element|List],Accumulator,Sum):-
NewAccumulator is Accumulator + Element,
(sum1(List,NewAccumulator,Sum);
List=[] -> Sum = NewAccumulator
).
You had List=[] -> Sum = Accumulator, that means that in case the tail of your list was empty, you took Accumulator, which is the sum of all previous elements before Element.
An alternative that preserves even more of your code is:
sum1([Element|List], Accumulator, Sum) :-
( NewAccumulator is Accumulator+Element,
sum1(List, NewAccumulator, Sum)
; List=[]
-> Sum is Accumulator+Element
).
However, I personally would prefer TA_intern's solution.

Creating a predicate in Prolog that sums the squares of only the even numbers in a list

I'm trying to figure out how to create a predicate in prolog that sums the squares of only the even numbers in a given list.
Expected output:
?- sumsq_even([1,3,5,2,-4,6,8,-7], Sum).
Sum = 120 ;
false.
What I know how to do is to remove all the odd numbers from a list:
sumsq_even([], []).
sumsq_even([Head | Tail], Sum) :-
not(0 is Head mod 2),
!,
sumsq_even(Tail, Sum).
sumsq_even([Head | Tail], [Head | Sum]) :-
sumsq_even(Tail, Sum).
Which gives me:
Sum = [2, -4, 6, 8]
And I also know how to sum all the squares of the numbers in a list:
sumsq_even([], 0)
sumsq_even([Head | Tail], Sum) :-
sumsq_even(Tail, Tail_Sum),
Sum is Head * Head + Tail_Sum.
But I can't seem to figure out how to connect these two together. I'm thinking I may have gone the wrong way about it but I'm not sure how to define proper relationships to get it to make sense.
Thanks!
Split your problem into smaller parts. As you already said, you have two different functionalities that should be combined:
remove odd numbers from a list (even)
sum all the squares of the numbers in a list (sumsq)
So, in the first place, use different predicate names for different functionalities:
even([], []).
even([Head | Tail], Sum) :-
not(0 is Head mod 2),
!,
even(Tail, Sum).
even([Head | Tail], [Head | Sum]) :-
even(Tail, Sum).
sumsq([], 0).
sumsq([Head | Tail], Sum) :-
sumsq(Tail, Tail_Sum),
Sum is Head * Head + Tail_Sum.
In a third predicate you can now combine the two subsequent smaller steps:
sumsq_even(List, Sum) :-
even(List, Even_List),
sumsq(Even_List, Sum).
In this rule, first the (input) list is reduced to even elements (Even_List) and after that the sum of the squares are calculated.
This is the result for your example:
sumsq_even([1,3,5,2,-4,6,8,-7], Sum).
S = 120.
Using clpfd and Prolog lambda write:
:- use_module(library(clpfd)).
:- use_module(library(lambda)).
zs_sumevensq(Zs, S) :-
maplist(\Z^X^(X #= Z*Z*(1-(Z mod 2))), Zs, Es),
sum(Es, #=, S).
Sample query as given by the OP:
?- zs_sumevensq([1,3,5,2,-4,6,8,-7], S).
S = 120.
You can actually do both tasks (filtering the even number and summing them up) at once:
:- use_module(library(clpfd)).
nums_evensumsq([],0).
nums_evensumsq([X|Xs],S0) :-
X mod 2 #= 0,
nums_evensumsq(Xs,S1),
S0 #= S1 + X * X.
nums_evensumsq([X|Xs],S) :-
X mod 2 #= 1,
nums_evensumsq(Xs,S).
Querying the predicate gives the desired result:
?- nums_evensumsq([1,3,5,2,-4,6,8,-7],S).
S = 120 ? ;
no
You can write it even shorter using if_/3 as defined here:
nums_evensumsq([],0).
nums_evensumsq([X|Xs],S0) :-
nums_evensumsq(Xs,S1),
Y #= X mod 2,
if_(Y = 0, S0 #= S1 + X * X, S0 #= S1).
Note that the comparison in the first argument of if_/3 is done with =/3 as defined here.
Once you mastered the basics, you could be interested to learn about builtins. Library aggregate, provides a simple way to handle lists, using member/2 as list elements 'accessor':
sumsq_even(Ints, Sum) :-
aggregate(sum(C), I^(member(I, Ints), (I mod 2 =:= 0 -> C is I*I ; C = 0)), Sum).

Comparing prolog lists

I'm trying to make a prolog predicate "comprueba(A,B,C,D,E)" that do the next statements:
All arguments are lists.
List D contains only the elements that are on A and B at the same time.
List D elements number of ocurrences must be the same ocurrences in A.
List E contains only the elements of A that are not on C and not on D.
List E elements number of ocurrences must be three times the occurrences in A.
There are no more elements than these in D or E.
The predicate must be true even if the order of D or E differs from A.
So here is my code:
comprueba(A,B,C,D,E) :- lista([A]),
lista([B]),
lista([C]),
lista([D]),
lista([E]),
inter(A,B,D),
checko(D,D,A,1).
%checke2(A,C,D,E),
%checko(E,E,A,3).
lista([]).
lista([_|T]) :-lista(T).
inter([], _, []).
inter([H1|T1], L2, [H1|Res]) :- memberof(H1, L2), inter(T1, L2, Res).
inter([_|T1], L2, Res) :- inter(T1, L2, Res).
checke2([],_,_,_).
checke2(A,C,D,E) :- subtract(A,D,X), subtract(A,C,Y), inter(X,Y,E).
count(_, [], N) :- N is 0.
count(X, [X|T], N1) :- count(X, T, N2), N1 is N2 + 1.
count(X, [Y|T], N) :- X \= Y, count(X, T, N).
memberof(X, [X|_]).
memberof(X, [_|T]) :- memberof(X,T).
checko([],_,_,_).
checko([H|T],L1,L2,N) :- count(H,L1,N1), count(H,L2,N2), N3 is N * N2, N1 = N3, checko(T,L1,L2,N).
After doing some testing I'm stucked, because I cannot get it true, if the list are not on the same order, e.g:
17 ?- comprueba([1,2,3,4,2,5,8,9],[2,3,4,7],[1,2,3,8],[2,2,3,4],[5,5,5,9,9,9]).
false.
18 ?- comprueba([1,2,3,4,2,5,8,9],[2,3,4,7],[1,2,3,8],[2,3,4,2],[5,5,5,9,9,9]).
true
So I really ask you for help to try to solve it, and continue with the next part, with E list.
Thanks you in advance.
PD:
sorry if the format is not the properly, it's my first post here :)
You could add a goal that describes D as any permutation of the 4th list (in the below example D2).
comprueba(A,B,C,D,E) :- lista([A]),
lista([B]),
lista([C]),
lista([D2]),
lista([E]),
inter(A,B,D2),
checko(D2,D2,A,1),
permutation(D2,D).
If you are not allowed to use permutation/2 from library(lists) permutation could look something like this:
% permutation(List1,List2)
% List2 is a permutation of List1
permutation([],[]).
permutation(Xs,[Z|Zs]) :-
element(Z,Xs,Ys),
permutation(Ys,Zs).
% element(X,List1,List2)
% X is element of List1, List2 = List1 without X
element(X,[X|Xs],Xs).
element(X,[Y|Ys],[Y|Zs]) :-
element(X,Ys,Zs).
With this additional goal your predicate comprueba/5 works with both of your queries.
?- comprueba([1,2,3,4,2,5,8,9],[2,3,4,7],[1,2,3,8],[2,2,3,4],[5,5,5,9,9,9]).
yes
?- comprueba([1,2,3,4,2,5,8,9],[2,3,4,7],[1,2,3,8],[2,3,4,2],[5,5,5,9,9,9]).
yes

Traverse a list in Prolog

I have a problem like this: find all elements in a list such that all element(s) immediately besides it is/are odd numbers.
For example
?- find([20,1,2,3,4,5,6,7,8,10], L).
L = [20, 2, 4, 6]
Normally in other languages I would traverse the list and check the condition, but I don't know how to "think" in Prolog in this scenario. How should I approach this?
visit the list considering the pair of head elements:
find([A,B|R], [A|T]) :-
is_odd(B),
... etc etc
You'll need to add obviously the base recursion case and the case when A must be discarded.
EDIT: a better solution based on CapelliCs suggestion (this uses the isodd predicate from below):
% if N0 and N2 are odd, cut, add N1 to the result and recurse
ff([N0,N1,N2|T], [N1|R]) :- isodd(N0), isodd(N2), !, ff([N1,N2|T], R).
% for any other case where the list has at least three members, cut and recurse
ff([_,N1,N2|T], R) :- !, ff([N1,N2|T], R).
% this is reached if the list has less that three members - we're done
ff(_, []).
% append and prepend '1' to the list to deal with the edges, call ff.
find(L, R) :- append(L, [1], L1), ff([1|L], R).
My old solution which keept track of the two previous values with extra arguments:
% isodd(+N)
% helper predicate that succeds for odd numbers.
isodd(N) :- mod(N, 2) =:= 1.
% find(+I, +N1, +N2, +R, -L)
% find/5 is the predicate doing the actual work.
% I is the input list, N1 and N2 are the numbers before the current one,
% R is the intermediate result list and L the result.
% we're done if the input list is empty
find([], _, _, R, R) :- !.
% check if N0 and N2 are odd to see if N1 should be appended to the list.
% if yes, do a cut, append N1 to the result and recurse.
find([N0|T], N1, N2, R, L) :-
isodd(N0), isodd(N2), !,
append(R, [N1], R1), find(T, N0, N1, R1, L).
% if N0 and N2 are not odd (and thus the cut in the previous clause isn't
% reached) just continue the recursion.
find([N0|T], N1, _, R, L) :- find(T, N0, N1, R, L).
% find(+I, -L)
% this predicate is the entry point - initialize the result list and the first
% values for N1 and N2, and append 1 to the input list so we don't need an extra
% predicate for dealing with the last item.
find(I, L) :- append(I, [1], I1), find(I1, 1, 0, [], L).

Counting from database

My database consists out of predicates like these:
road(1,2,geel).
road(2,3,blauw).
road(1,3,geel).
where the first 2 numbers are points. I have to check wether every point has a even number of roads. I managed to do it with the following code, but somehow I think there's a better way of doing this.
% Count(Element, List, Occurences) => Counts the amount of occurences of Element in the given List
count(_, [], 0).
count(X, [X | T], N) :-
!, count(X, T, N1),
N is N1 + 1.
count(X, [_ | T], N) :-
count(X, T, N).
checkRoad :-
findall(Point,(weg(Point,_,_) ; weg(_,Point,_)),List),
list_to_set(List,K),
foreach( (member(P,K), count(P, List,N)), N mod 2 =:= 0 ).
I think this approach would have better performance:
checkRoad:-
findall(Point,(road(Point,_,_) ; road(_,Point,_)),List), % renamed wge/3 with road/3
msort(List, SList),
checkEven(none, SList).
checkEven(_, []).
checkEven(Item, [Item|SList]):-
!,
checkOdd(Item, SList).
checkEven(_, [Item|SList]):-
checkOdd(Item, SList).
checkOdd(Item, [Item|SList]):-
checkEven(Item, SList).
If you first sort all the points then you can just traverse this sorted list once to test whether every point appears in an even number of roads.

Resources