Prolog Or(;) Rule Return Multiple Result - prolog

i have define a rule with or operator but it return multiple true or false.
isloanaccept(Name,Guarantor,LoanType,LoanAmount,LoanTenure)
:- customer(Name,bank(_),customertype(_),
citizen(Ci),age(Age),credit(C),
income(I),property(_),bankemployee(_)),
Ci == 'malaysian',
Age >= 18,
C > 500,
I > (LoanAmount / LoanTenure) / 12,
isguarantor(Guarantor,Name),
ispersonalloan(LoanType,LoanAmount,LoanTenure);
ishouseloan(LoanType,LoanAmount,LoanTenure);
isbusinessloan(LoanType,LoanAmount,LoanTenure);
iscarloan(LoanType,LoanAmount,LoanTenure).
Actually, i need to check whether the loan type is fulfill the particular loan requirement and combine with general rule.
In other words, i need to define the rule above like this.
Ci == 'malaysian', Age >= 18,C > 500,
I > (LoanAmount / LoanTenure) / 12,
isguarantor(Guarantor,Name)
Or with (ispersonalloan(LoanType,LoanAmount,LoanTenure);
ishouseloan(LoanType,LoanAmount,LoanTenure);
isbusinessloan(LoanType,LoanAmount,LoanTenure);
iscarloan(LoanType,LoanAmount,LoanTenur)
It should return 1 true/false rather than multiple statement in the command line.
Each of the or rule return 1 boolean value which is not i want after have checked the rule in command line. I need to have like this (General Rule & (Multiple Or Rule) ).
How to combine several or rule which return 1 boolean value ?
Please help.
Thanks.

Just surround all your "or'ed" goals with once.
e.g.
once(
ispersonalloan(LoanType,LoanAmount,LoanTenure);
ishouseloan(LoanType,LoanAmount,LoanTenure);
isbusinessloan(LoanType,LoanAmount,LoanTenure);
iscarloan(LoanType,LoanAmount,LoanTenure)
).
Now, the "or'ed" goals either succeed or fail.

First of all you should put ( and ) around your target combined with ;. Because currently it interprets it like disjunction of customer(...),...,isguarantor(Guarantor,Name), ispersonalloan(...), ishouseloan(...), ..., iscarloan(...). That's because different priorities of operators , and ;.
Actually ; - means real "or", not "mutual exclusive or" and not "in other case". So if "ishouseloan" can' succeed together with "ispersonalloan" than you'll have several successful targets. In this example once/1 may help (as well not(not(...))), but you can try to get prolog deeper with your task and specify non-everlapping targets like (I do some personal assumptions about overlapping isXXX):
isloan(LT, Am, T):-
(ishouseloan(LT,Am,T)
;iscarloan(LT,AM,T)
;not((ishouseloan(LT,Am,T);iscarloan(LT,AM,T))),
(ispersonalloan(LT,Am,T)
;isbusinessloan(LT,Am,T)
)
)
In this case you should be able to generate all loans when your LT, Am and T is not yet bound to specific values and those isXXX can bind free variables.

Related

Prolog Set of and Length predicate

Im trying to work on this prolog rule which is Given a star, check whether he or she is linked with three different movies. If true, return the name of the movies, else return false.
database:
starsin(spiderman,tom_holland).
starsin(captain_america,tom_holland).
starsin(avengers,tom_holland).
starsin(parasite,kang_ho).
I tried using set of function in my rule which is:
checkStarinMovie(Star,Movie):-setof(X,starsin(X,Star),Movie).
% to the the rule write this in the query: checkStarinMovie(tom_holland,Movie).
but it would only return the list of movies that the star is in.
I want to be conditional that the star need to be in 3 movies or over. So in this case the star "kang_ho" will return false if you run in the query.
Is there anyway I could use length predicate to set a condition of the rule?

I want to sum in SWI-Prolog depending on certain user answers

I need to sum in a variable depending on certain user answers and I'm starting to know Prolog syntax and paradigm.
Right now, I can read user data, and print it too, but I lack a way to accumulate the results, because right now the results are inconsistent.
What I have now is:
inicio :-
write('¿You have overweight yes/no?'),
read(R1),
write('¿Are you a smoker yes/no?'),
read(R2),
write('¿Do you have some direct relative with diabetes?'),
read(R3),
Risk is 0,
( R1 = yes -> Risk is Risk + 2 ; Risk is Risk + 0 ),
imprimir(['The result is ', Risk]),
( R2 = yes -> Risk is Risk + 1 ; Risk is Risk + 0 ),
imprimir(['The result is ', Risk]),
( R3 = yes -> Risk is Risk + 3 ; Risk is Risk + 0 ),
imprimir(['The result is ', Risk]).
imprimir([]).
imprimir([Term| Terms]) :-
write(Term),
imprimir(Terms).
I'm going to show you a fundamentally different way of approaching this program that leverages Prolog a little better. First, let's make a table of the penalties. Making tables for your program's configuration is often a useful thing to do:
risk_penalty(overweight, 2).
risk_penalty(smoker, 1).
risk_penalty(diabetes, 3).
Now that we have a uniform way of thinking about the problem, let's see if we can make a uniform way of getting information from the user. Let's use the dynamic store to keep track of what the user has told us, because it will simplify querying later:
:- dynamic risk/2.
ask(Prompt, Fact) :-
format('~a [yes/no]> ', [Prompt]),
read(Response),
assertz(risk(Fact, Response)).
Now we have a little predicate we can use to interview the user. This kind of print-read-assert function is pretty common in small expert systems like yours, because it helps you separate the logic of the system from its front-end. When you do ask('Do you have X?', has_x), the dynamic store will either receive risk(has_x, yes) or risk(has_x, no) depending on which the user has entered. It also gives you a natural place to make the user input more robust, by checking it and re-asking if you get something weird.
Now we can do your initial loop a little more cleanly:
inicio :-
ask('Are you overweight?', overweight),
ask('Are you a smoker?', smoker),
ask('Do you have some direct relative with diabetes?', diabetes).
This just does the interview portion. Now if you run through it once, say answering "yes", "no", "yes", then the database will contain these facts:
?- risk(Factor, Response).
Factor = overweight,
Response = yes ;
Factor = smoker,
Response = no ;
Factor = diabetes,
Response = yes.
What we need to do now is select out "yes" factors and then look up their penalties and add them up. To do this, we can use findall/3, which takes a Template, a Goal, and gives back a result list:
?- findall(risk(Factor, Response), risk(Factor, Response), Responses).
Responses = [risk(overweight, yes), risk(smoker, no), risk(diabetes, yes)].
As you can see, I used the same template and goal here, just to see all the results, but we can put "yes" in to filter it down to just the risk factors we care about:
?- findall(risk(Factor), risk(Factor, yes), Responses).
Responses = [risk(overweight), risk(diabetes)].
Now you can see that the Template (first argument) is just some arbitrary structure populated with the variables that findall/3 found by running Goal, the second argument. So we could also just obtain the list of penalty values, if we look them up inside the Goal query. Like this:
?- findall(Penalty, %% <- template
(risk(Factor, yes), risk_penalty(Factor, Penalty)), %% <- goal
Penalties). %% <- result
Penalties = [2, 3].
We can then follow this with just sumlist/2 to add everything up:
?- findall(Penalty,
(risk(Factor, yes), risk_penalty(Factor, Penalty)),
Penalties),
sumlist(Responsa, Score).
Responsa = [2, 3],
Score = 5.
Now we can finish the inicio/0 predicate:
inicio :-
retractall(risk(_, _)),
ask('Are you overweight?', overweight),
ask('Are you a smoker?', smoker),
ask('Do you have some direct relative with diabetes?', diabetes)
findall(Penalty,
(risk(Factor, yes), risk_penalty(Factor, Penalty)), Penalties),
sumlist(Penalties, Score),
format('The result is ~a~n', [Score]).
This now looks like this when run:
?- inicio.
Are you overweight? [yes/no]> yes.
Are you a smoker? [yes/no]> |: no.
Do you have some direct relative with diabetes? [yes/no]> |: yes.
The result is 5
true.
I hope you find the result pleasing to look at, much less procedural, and easier to modify and maintain.

Prolog, print employees with same names

This is my first time using Prolog.
I have employees:
employee(eID,firstname,lastname,month,year).
I have units:
unit(uID,type,eId).
I want to make a predicate
double_name(X).
that prints the last names of the employees with the same first name in the unit X.
I am doing something like this :
double_name(X) :-
unit(X,_,_eID),
employee(_eID,_firstname,_,_,_),
_name = _firstname,
employee(_,_name,_lastname,_,_),
write(_lastname).
But it prints all the employees in the unit.
How can i print only the employees with the same name ?
unit(unit_01,type,1).
unit(unit_01,type,2).
unit(unit_01,type,3).
employee(1,mary,smith,6,1992).
employee(2,fred,jones,1,1990).
employee(3,mary,cobbler,2,1995).
double_name(Unit) :-
unit(Unit,_,Eid_1),
employee(Eid_1,Firstname,Lastname_1,_,_),
unit(Unit,_,Eid_2),
Eid_1 \= Eid_2,
employee(Eid_2,Firstname,Lastname_2,_,_),
write(Firstname),write(","),write(Lastname_1),nl,
write(Firstname),write(","),write(Lastname_2).
Variables in Prolog typically start with an upper case letter, but starting them with and underscore is allowed, but not typical.
In double_name/2 the predicates like
unit(Unit,_,Eid_1)
employee(Eid_1,Firstname,Lastname_1,_,_)
are used to load the values from the facts into variables while pattern matching (via unification) that the bound variables match with the fact.
To ensure that a person is not compared with themselves.
Eid_1 \= Eid_2
and to make sure that two people have the same first name the same variable is used: Firstname.
The write/1 and nl/0 predicates just write the result to the screen.
Example:
?- double_name(unit_01).
mary,smith
mary,cobbler
true ;
mary,cobbler
mary,smith
true ;
false.
Notice that the correct answer is duplicated. This can be resolved.
See: Prolog check if first element in lists are not equal and second item in list is equal
and look at the use of normalize/4 and setof/3 in my answer
which I leave as an exercise for you.

Prolog retract more then one fact and then assert

:- dynamic flat/3.
addr('Nollendorfstr',5).
addr('Nollendorfstr',14).
addr('Nollendorfstr',18).
addr('Maxplanckstr',2).
flat([16,12,4],400.35, addr('Nollendorfstr',14)).
flat([14,13,4],380.00, addr('Nollendorfstr',18)).
flat([20,18,4,5],650.80, addr('Nollendorfstr',5)).
flat([9,17,19,20],870.70, addr('Maxplanckstr',2)).
We have for each flat three arguments. So flat(array of rooms, price, address).
changeprice(Street):-
retract(flat(Rooms,Price,addr(Street,Num))),
Newprice is Price - (Price / 10),
asserta(flat(Rooms,Newprice,addr(Street,Num))).
I'd like to change the price for all flats which are in this street.
so when I put there changeprice('Nollendorfstr').
It will just change one of them.
How can I change all of them?
You can use either a failure-driven loop (which gets its name from the use of explicit failure after processing an item to backtrack into processing the next item):
changeprice(Street) :-
retract(flat(Rooms,Price,addr(Street,Num))),
Newprice is Price - (Price / 10),
asserta(flat(Rooms,Newprice,addr(Street,Num))),
fail.
changeprice(_).
Or the de facto standard forall/2 meta-predicate (where the first argument can be interpreted as a generator of candidate solutions that are verified by the second argument, the test part):
changeprice(Street) :-
forall(
retract(flat(Rooms,Price,addr(Street,Num))),
( Newprice is Price - (Price / 10),
asserta(flat(Rooms,Newprice,addr(Street,Num)))
)
).
In this particular case, both solutions are equivalent. But note that failure-driven loops and forall/2 calls have different semantics. A failure-driven loop can mask an unexpected failure. But a forall/2 call will fail instead of succeeding if the second argument fails.

Creating a counter Prolog

I'm trying to make a counter out of the following code:
contador([], 0,[]).
contador([via(A,_,_)|R], Tot,Regiao):-
cidade(A,_,_,Reg1),Reg1==Regiao,
Tr is Tot + 1,
contador(R,Tr,Regiao).
Given my list's format and cidade:
L=[via(porto,lisboa,_),via(braga,faro,_),via(guimaraes,santarem,_)]
cidade(lisboa,_,_,A)
Why isn't it working?
cidade(porto,portugal,40,litoral).
cidade(braga,portugal,350,interior).
cidade(guimares,portugal,40,litoral).
cidade(alverca,portugal,30,valedotejo).
cidade(santarem,portugal,25,valedotejo).
cidade(faro,portugal,20,litoral).
cidade(sevilha,espanha,60,interior).
With this list:
A = [via(porto, braga, 5), via(braga, guimaraes, 9), via(guimaraes, alverca, 7), via(alverca, faro, 10)] ;
I'm trying to do the following:
?-contador(A,Tot,litoral).
false.
My objective is to count the cities that have A(cidades(_,_,_,A)) as a parameter.
There are several issues in the code you presented.
1)
First, a remark, in Prolog, "=" is not an assignment like in C/C++ and other imperative languages. That means you can not type:
A=[...].
contador(...).
but use a single query:
A=[...],contador(...).
2)
These two statements are possibly an error:
contador([], 0,[]).
contador([via(A,_,_)|R], Tot,Regiao):-...
because third argument is in the first one a list, and is not in the second.
3)
Finally, what you call a "contador/counter" ( and that is, probably, better called an "accumulator" ) is not correctly "finished". When the original list is exhausted, the statement:
contador([], 0,[]).
is applied, but this statement request counter equal to cero. Probably, it will be better something like:
contador([], R, _) :- ... /* do something with R */
4)
These two statements:
cidade(A,_,_,Reg1),Reg1==Regiao,
can be unified in a single one:
cidade(A,_,_,Regiao),
5)
In general, try to skip "is" operator. By example, replace:
Tr is Tot + 1,
with:
succ(Tot,Tr),
6)
If statement:
cidade(A,_,_,Reg1),Reg1==Regiao,
fails, you have not an alternative rule.

Resources