Predicate in prolog - prolog

I need to define a predicate in prolog which takes a list as input and sums the squares of numbers >= 5 and subtract sum of absolute value of numbers <=2.
This is what I have currently :-
pred([], 0).
pred([Head|Tail], Result) :-
gr85(Head),
pred(Tail, Total),
Result is Head*Head + Total.
pred([Head|Tail], Result) :-
leq2(Head),
pred(Tail, Total),
Result is Total - Head.
gr85(Number):-
Number >= 5.
leq2(Number):-
Number =< 2.
My question is how do I exclude anything between 2 and 5. If I input 3 in the list, it returns false.
Expected input
pred([3,6,2,-1], Result).
Expected output
Result= 33 (6*6 -2-1)

add a 'skip all' clause:
weird_sum([_|Tail], Result) :- weird_sum(Tail, Result).
I think you will gain some insight into Prolog working when you analyze where this clause should be added.

Maybe it is simpler if you keep it more simple, you can use accumulator to make it simpler. And also it is maybe less typing if you move the condiditional out, like this;
weird_sum(List, Sum) :-
weird_sum(List, 0, Sum).
weird_sum([], Acc, Sum) :- Sum is Acc.
weird_sum([X|Xs], Sum0, Sum) :-
once( weird_sum_helper(X, Sum0, Sum1) ),
weird_sum(Xs, Sum1, Sum).
weird_sum_helper(X, Acc, Acc + X*X) :- X >= 5.
weird_sum_helper(X, Acc, Acc - abs(X)) :- X =< 2.
weird_sum_helper(X, Acc, Acc) :- X < 5, X > 2.
But the actual answer to your original question is in the last line above, just say that when X is smaller than 5 AND X is larger than 2 than you don't do anything.
But I get different answer from 33 because when number is negative and you subtract you get positive not negative but I am not sure if this is what you mean.
I used once once just because you want to do this only once since the three conditions are excluding each other but somehow Prolog doesn't know that it should only take one and tries the others too.

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.

count number of times Y happen in fact(X,Y) in Prolog Programming

I am new in prolog programming and I trying to experiment with it
I have 2 questions
suppose I have some facts
fact('A', 'B').
fact('A', 'C').
fact('A', 'D').
when I write fact('A', X), the X will be of 3 values 'B', 'C', and 'D'
how do I write a rule to get how many times 'A' has a pair, in this case the answer will be 3
suppose I have a fact
a([1,2,4,5]).
how do I write a rule to loop through the elements to add them, i.e. find the sigma
thank you
If you just need to count solutions and you are using SWI-Prolog, use aggregate_all:
?- aggregate_all(count, fact('A', _), N).
This way you don't need to existentially qualify variables, you can just "skip" it from the query by using an anonymous variable in its place.
This also correctly counts 0 solutions without further code tricks:
?- aggregate_all(count, fail, N).
To sum a list, you can use sum_list/2 from library(lists). Note that you could also use library(aggregate) for this, if you have a backtrackable predicate (a table of facts) instead of a list. This is also the more usual way to represent a "list" of things in the database.
a(1).
a(2).
a(4).
a(5).
This is how you'd usually find the sum of all rows in your table:
?- aggregate_all(sum(X), a(X), Sum).
Sum = 12.
You can have more columns in your table, of course.
p(a, 3).
p(b, 2).
p(b, 1).
p(a, 2).
p(a, 0).
You can sum those like this:
?- aggregate_all(sum(X), p(a, X), Sum).
Sum = 5.
?- aggregate_all(sum(X), p(b, X), Sum).
Sum = 3.
?- aggregate_all(sum(X), p(foo, X), Sum).
Sum = 0.
?- aggregate_all(sum(X), p(_, X), Sum). % total sum
Sum = 8.
In contrast, you can use aggreagate to get the different groups:
?- aggregate(sum(X), p(A, X), Sum).
A = a,
Sum = 5 ;
A = b,
Sum = 3.
You can collect a list of solutions and get the list's size:
?- findall(X, fact('A',X), List), length(List,Count).
List = ['B', 'C', 'D'],
Count = 3.
or better use the aggregation predicates from library(aggregate):
?- aggregate(count, X^fact('A',X), Count).
Count = 3.
Note caret-notation X^ to signify that X is not a variable over which aggregate(count, Goal_with_X, Count). should backtrack (giving three times a count of 1) but a free variable of the Goal_with_X which varies over the bag of solutions whose size shall be assessed.

prolog - confusing behaviour of prolog code (beginner)

Here is my code.
equals2(X,Y,N,I):- X is Y,I is N+1; I is N.
elemNum(X,[],0).
elemNum(X,[Y|Ys],N) :- elemNum(X,Ys,N1),equals2(X,Y,N1,I),N is I.
lemNum first argument is element from array, second is array. It counts the number of elements in array.
Then in console
| ?- elemNum(1,[1,2,3,1,1],N),N<2.
N = 1 ?
yes
I am sure than my function elemNum works just fine. How its possible that in console this assertion returns 1?
Thanks for help
Non sure to understand what do you want ... but I suppose that you want count the number of element in the list (second argument of elemNum/3) that are equals to the first argument.
If so, you should modify equals2/4 as follows
equals2(X,Y,N,I):- X is Y,I is N+1; X \== Y, I is N.
or better (IMHO) split it in 2 different clauses
equals2(X,X,N,I):- I is N+1.
equals2(X,Y,N,N):- X \== Y.
With your equal2/4, the second or case (I is N) is executed (in backtracking) even when X is equal to Y so elemNum(1,[1,2,3,1,1],N) unifiy N with 3, 2, 2 again, 1, 2, 1, 1 again and 0.
Regarding elemNum/3, works but you can semplify it (avoiding a warning) as
elemNum(_,[],0).
elemNum(X,[Y|Ys],I) :- elemNum(X,Ys,N1), equals2(X,Y,N1,I).
or you can rewrite it, avoiding the use of equals2/4 as
elemNum(_, [], 0).
elemNum(X, [X | Ys], I) :- elemNum(X, Ys, I0), I is I0+1.
elemNum(X, [Y | Ys], I) :- X \== Y, elemNum(X, Ys, I).

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).

List building problems

For my program I need to make a list of lists, with each sublist containing 2 numbers, X and Y along with the sum and product of these 2 numbers.
So far I have the following:
genList(95, X,[]):-!.
genList(N, X,[[X,Y,Sum,Product]|Xs]):-
Y is N+1,
Sum is X+Y,
Sum<101,
Product is X*Y,
N1 is N+1,
genList(N1, X,Xs).
This works just fine for my test case of genList(5,5,Q).
However, I'm having trouble making it work for any starting number.
The goal is to find every pair of numbers where sum<= 100. So running through the above for one starting value, X would find every pair 1 < X < Y, where sum<=100, and running through it with all numbers 2-N would give a complete list of possible pairs.
For those interested, the problem I'm working through is the sum/product problem, described here (Second on the page)
If anyone could help with this it would be greatly appreciated!
Also, no built in prolog predicates are able to be used, hence the complicated way of doing this rather than with a findall.
A small extract of the output produced by this predicated is as follows:
[[5,6,11,30],[5,7,12,35],[5,8,13,40],[5,9,14,45],[5,10,15,50],[5,11,16,55],[5,12,17,60],[5,13,18,65],[5,14,19,70],[5,15,20,75],[5,16,21,80],[5,17,22,85],[5,18,23,90],[5,19,24,95],[5,20,25,100],[5,21,26,105],[5,22,27,110], ...
EDIT:
Ok, so after some editing, here is the latest version of my code.
I think it's very close, but there's still something not quite right.
It cycles through number pairs, but requires the use of ";" to view all the answers, which isn't what I want. Additionally, it returns false after all the answers are exhausted. I just can't figure it out.
Also, it gives a complete answer in the middle, but then removes a sublist each time until I'm left with only the last set of pairs.
E.g. genList(0,48,48,Q). gives me:
[[48,49,97,2352],[48,50,98,2400],[48,51,99,2448],[48,52,100,2496]]
[[48,49,97,2352],[48,50,98,2400],[48,51,99,2448],[48,52,100,2496],[49,50,99,2450],[49,51,100,2499]]
[[48,49,97,2352],[48,50,98,2400],[48,51,99,2448],[49,50,99,2450],[49,51,100,2499]]
[[48,49,97,2352],[48,50,98,2400],[49,50,99,2450],[49,51,100,2499]]
[[48,49,97,2352],[49,50,99,2450],[49,51,100,2499]]
[[49,50,99,2450],[49,51,100,2499]]
false.
As you can see, a sublist gets removed each time, I just can't see why!
You can exploit Prolog backtracking here. Just state what you want. For example you could say:
I want X to be between 1 and 100.
I want Y to be between 1 and min(100 - X, X).
then I want their pair
Let's look at what a validPair/1 predicate would look like:
validPair(X-Y) :-
between(1, 100, X),
Limit is min(100 - X, X),
between(1, Limit, Y).
You can just call it with
?- validPair(X).
and browse results with ;, or build a list of all the matching pairs with findall/3.
Edit: even with recursion, we can keep our statements:
I want X to be between 1 and 100.
I want Y to be between 1 and min(100 - X, X).
then I want their pair
So, an idea to do it would be to set up a worker predicate:
validPair(Result) :-
validPair(0, 0, Result).
validPair(X, Y, R) :-
...
then set up the base case:
validPair(101, _Y, []) :- !.
and in the worker predicate, to implement the statements we made with some conditions:
validPair(X, Y, [SomeStuff|R]) :-
X =< 100,
Limit is min(100 - X, X),
Y =< Limit,
!,
% we can go on and increment Y once we're finished
validPair(X, NextY, R).
validPair(X, Y, R) :-
% if we come here that means that Y is finished growing and
% we have to increment X
NextX is X + 1,
validPair(NextX, 0, R).
I have a feeling you're tackling the problem the wrong way; I must admit I don't really understand what your predicate is doing.
The goal is to find every pair of numbers where sum<= 100.
Assuming you mean unordered pairs of non-negative integers, that's
between(0, 100, Sum),
between(0, Sum, X),
Y is Sum - X,
X =< Y.
The set of all such pairs (as a list) can then be constructed with findall/3.
You could also do this using CLP(fd):
use_module(library(clpfd)).
[X, Y, Sum] ins 0..100,
X #=< Y,
X + Y #= Sum,
label([X,Y,Sum]).

Resources