Prolog beginner here - prolog

hasAccount(Person,Bank,Amount) – the Person has an account at the Bank with the balance Amount,
lives(Person,City) – the Person lives in the City,
created(Person,Bank,Month,Year) – the Person opened an account at the Bank in Month of the Year.
With the predicates above, I want to try few things, say printing a list of names with accounts they have, I tried the following query.
?- hasAccount(Someone, Bank1, Balance1), hasAccount(Someone, Bank2, Balance2), not Bank1 = Bank2.
Someone = ann
Bank1 = metro_credit_union
Balance1 = 1000
Bank2 = toronto_dominion
Balance2 = 12000
Yes (0.00s cpu, solution 1, maybe more)
Someone = ann
Bank1 = toronto_dominion
Balance1 = 12000
Bank2 = metro_credit_union
Balance2 = 1000
Can you please explain why do I have reversed list again and what I can do to prevent this happening? Thank you!

why I have reversed list again
it happens since you're not stating in your query enough info to break symmetry.
You can use instead
?- hasAccount(Someone, Bank1, Balance1), hasAccount(Someone, Bank2, Balance2), Bank1 #< Bank2.
or some builtin, like findall/3 (or bagof/3)

Related

Prolog - bagof - case of no results

I have activity-users relations:
% Signature: activity(Name,Day)/2
% Purpose: describe an activity at the country club and the day it takes place
%
activity(swimming,sunday).
activity(ballet,monday).
activity(judu,tuesday).
activity(soccer,wednesday).
activity(art,sunday).
activity(yoga,tuesday).
% Signature: participate(Child_name,Activity)/2
% Purpose: registration details
%
participate(dany,swimming).
participate(dany,ballet).
participate(dana,soccer).
participate(dana,judu).
participate(guy,judu).
participate(shai,soccer).
I want to create query to get all the participants of activity.
So I use bagof:
activity_participants_list(Activity_Name,List) :- ( bagof(X, participate(X,Activity_Name), List)).
But I want to get List = [] when query: activity_participants_list(dancing,List)
So I added ; List = [] to the end of the function but then
when query
activity_participants_list(A,B) i also get empty list as an answer
And query activity_participants_list(A, []) return true.
I also would like to get yoga with empty list as an answer to activity_participants_list(A,B)
What is the right way to do it?
As you found, bagof fails when the goal has no solutions. This is dumb design, unfortunately. As you also found, ; is tricky to use. Try not to be tempted to use it.
findall works similarly to bagof, and it does give an empty list of results if the goal has no solutions:
activity_participants(Activity, Participants) :-
activity(Activity, _), % we are interested in a concrete activity
findall(Participant, participate(Participant, Activity), Participants).
?- activity_participants(Activity, Participants).
Activity = swimming,
Participants = [dany] ;
Activity = ballet,
Participants = [dany] ;
Activity = judu,
Participants = [dana, guy] ;
Activity = soccer,
Participants = [dana, shai] ;
Activity = art,
Participants = [] ;
Activity = yoga,
Participants = [].
The role of the activity(Activity, _) goal is to bind Activity, this basically forces findall to succeed several times, grouped by activity. If we removed this goal, we wouldn't group by activity, and we would just get a list of all the people participating in any activity:
?- activity_participants(Activity, Participants).
Participants = [dany, dany, dana, dana, guy, shai].

Differentiating two people prolog

I'm new to Prolog, and I'm having trouble understanding the OR operator ";"
so this is an example below:-
/*attributes(Person,Eats,Footwear).*/
attributes( personA,
eats(fried;baked),
footwear(shoes;slippers)
).
attributes( personB,
eats(roasted;baked),
slippers
).
person(Person, Eats, Footwear) :-
attributes(Person,
Eats,
Footwear
).
so I need to differentiate between personA and personB, eg if I put in the query
person(Person, roasted, _).
since only personB has the attribute roasted, it should return Person = personB
then, eg if I put in the query
person(Person, baked, _).
since both A and B has the attribute baked, it should return Person = personA Person = personB
Can anyone explain how to properly make the rules for this. Thanks.
To get the result you want, you have to rewrite the knowledge base in this way (you cannot use ; inside a term):
footwear(personA,slippers).
footwear(personA,shoes).
footwear(personB,slippers).
eats(personA,fried).
eats(personA,baked).
eats(personB,roasted).
eats(personB,baked).
Then you query:
?- eats(Person,roasted).
Person = personB.
?- eats(Person,baked).
Person = personA
Person = personB
If you are into probabilistic programming, you can translate this program into a probabilistic program to get maybe some interesting results.

How to calculate original loan amount without year terms?

https://www.moneysmart.gov.au/tools-and-resources/calculators-and-apps/savings-goals-calculator
I want to get result like above calculator when I select:
I want to save: 6000
I want to spend it: As soon as possible
Starting balance: 0
Interest rate : 10%
Regular savings: 1000 Monthly
But I am getting not correct result using this code:
loan = 6000.0
interest = 10.0
monthly_payment = 1000.0
i =0.0
record = []
count = 1
add_interst = 0.0
while( loan>=0)
i = interest/(100*12)*loan
loan=i+(loan)-(monthly_payment);
add_interst = add_interst + i
end
puts add_interst
I am getting 181.42163384701658 which should be 168. I don't know where I am wrong.
The code doesn't work because you are doing the opposite of what the link you reference is doing. What they are calculating is saving interest, what you are calculating is loan interest.
Basically, this is how you should define the variables.Also, as others have pointed out, it is good to use BigDecimal to calculate money:
require 'bigdecimal'
balance = 0.to_d
interest = 10.to_d/1200.to_d
regular_saving = 1000;
goal =6000;
i = 0;
added_interest = 0
So, to correct things, you have to start from the starting balance (i.e 0) and start incrementing. Something like this:
while balance < goal
balance += regular_saving;
i = balance * (interest);
balance +=i;
added_interest+=i;
end
Note also, that in the last year you don't need to pay the full saving amount. You only need to pay to reach the goal. For that, you need to add a conditional statement to check goal - balance < regular_saving. If this was the case, the interest should be calculated in terms of the balance that should be paid (slightly less than the goal).

Prolog. Count members in database or List

I have a database i want to write a query to return the total members in the database
member(paul,100). member(john,101). member(ryan,102). member(jabouki,103).
Should Return 4
I also have a list and i want to return the number of persons in the list
memberlist([ant,cat,sat,bat]).
Should Return 4
Query I have thus far that only return the items in the database and list.
member(MemberName,_).
memberlist(Y)
findall(X, member(X,_), L), length(L,N).
Will give you the following results:
L = [paul, john, ryan, jabouki],
N = 4.
Greetings
Solick

Regroup By in PigLatin

In PigLatin, I want to group by 2 times, so as to select lines with 2 different laws.
I'm having trouble explaining the problem, so here is an example. Let's say I want to grab the specifications of the persons who have the nearest age as mine ($my_age) and have lot of money.
Relation A is four columns, (name, address, zipcode, age, money)
B = GROUP A BY (address, zipcode); # group by the address
-- generate the address, the person's age ...
C = FOREACH B GENERATE group, MIN($my_age - age) AS min_age, FLATTEN(A);
D = FILTER C BY min_age == age
--Then group by as to select the richest, group by fails :
E = GROUP D BY group; or E = GROUP D BY (address, zipcode);
-- The end would work
D = FOREACH E GENERATE group, MAX(money) AS max_money, FLATTEN(A);
F = FILTER C BY max_money == money;
I've tried to filter at the same time the nearest and the richest, but it doesn't work, because you can have richest people who are oldest as mine.
An another more realistic example is :
You have demands file like : iddem, idopedem, datedem
You have operations file like : idope,labelope,dateope,idoftheday,infope
I want to return operations that matches demands like :
idopedem matches ideope.
The dateope must be the nearest with datedem.
If datedem - date_ope > 0, then I must select the operation with the max(idoftheday), else I must select the operation with the min(idoftheday).
Relation A is 5 columns (idope,labelope,dateope,idoftheday,infope)
Relation B is 3 columns (iddem, idopedem, datedem)
C = JOIN A BY idope, B BY idopedem;
D = FOREACH E GENERATE iddem, idope, datedem, dateope, ABS(datedem - dateope) AS datedelta, idoftheday, infope;
E = GROUP C BY iddem;
F = FOREACH D GENERATE group, MIN(C.datedelta) AS deltamin, FLATTEN(D);
G = FILTER F BY deltamin == datedelta;
--Then I must group by another time as to select the min or max idoftheday
H = GROUP G BY group; --Does not work when dump
H = GROUP G BY iddem; --Does not work when dump
I = FOREACH H GENERATE group, (datedem - dateope >= 0 ? max(idoftheday) as idofdaysel : min(idoftheday) as idofdaysel), FLATTEN(D);
J = FILTER F BY idofdaysel == idoftheday;
DUMP J;
Data in the 2nd example (note date are already in Unix format) :
You have demands file like :
1, 'ctr1', 1359460800000
2, 'ctr2', 1354363200000
You have operations file like :
idope,labelope,dateope,idoftheday,infope
'ctr0','toto',1359460800000,1,'blabla0'
'ctr0','tata',1359460800000,2,'blabla1'
'ctr1','toto',1359460800000,1,'blabla2'
'ctr1','tata',1359460800000,2,'blabla3'
'ctr2','toto',1359460800000,1,'blabla4'
'ctr2','tata',1359460800000,2,'blabla5'
'ctr3','toto',1359460800000,1,'blabla6'
'ctr3','tata',1359460800000,2,'blabla7'
Result must be like :
1, 'ctr1', 'tata',1359460800000,2,'blabla3'
2, 'ctr2', 'toto',1359460800000,1,'blabla4'
Sample input and output would help greatly, but from what you have posted it appears to me that the problem is not so much in writing the Pig script but in specifying what exactly it is you hope to accomplish. It's not clear to me why you're grouping at all. What is the purpose of grouping by address, for example?
Here's how I would solve your problem:
First, design an optimization function that will induce an ordering on your dataset that reflects your own prioritization of money vs. age. For example, to severely penalize large age differences but prefer more money with small ones, you could try:
scored = FOREACH A GENERATE *, money / POW(1+ABS($my_age-age)/10, 2) AS score;
ordered = ORDER scored BY score DESC;
top10 = LIMIT ordered 10;
That gives you the 10 best people according to your optimization function.
Then the only work is to design a function that matches your own judgments. For example, in the function I chose, a person with $100,000 who is your age would be preferred to someone with $350,000 who is 10 years older (or younger). But someone with $500,000 who is 20 years older or younger is preferred to someone your age with just $50,000. If either of those don't fit your intuition, then modify the formula. Likely a simple quadratic factor won't be sufficient. But with a little experimentation you can hit upon something that works for you.

Resources