use operator as constructor in prolog - prolog

I'm writing a function called leagalCourse, it takes just one parameter, a course list. A course like, for example, john+mary+94 would represent a project done by John and Mary with a mark of 94.
It should be true if the course data is “legal”, which means that it must not have a project with the same name twice such as john+john+70.
There also must not be two projects in the list containing the same pair of students. So if there’s a project harry+ron+82 in the list, it would be illegal for the list also to contain harry+ron+90 or ron+harry+63.
There is a sample output:
?- legalCourse([one+two+3,four+five+6,one+six+7]).
true.
?- legalCourse([one+two+3,four+four+6,one+six+7]).
false.
?- legalCourse([one+two+3,four+five+6,one+two+7]).
false.
?- legalCourse([one+two+3,two+one+6,one+six+7]).
false.
This is what I tried:
legalCourse([]).
legalCourse(X) :-
diffName(X).
legalCourse([Project|M]):-
diffName(Project),
not(samePair([Project|M])),
legalCourse(M).
diffName(Name1+Name2+_) :-
Name1 \= Name2.
/*can not have duplicated group*/
samePair([Name1+Name2+_|More]) :-
append([[head],tail,More]),
member(Name1,[head]),
member(Name2,[head]).
The function partially worked before I added the samePair predicate.

I think this works, you need to switch the vars and check both are different in check_no_dups/1.
legalCourse(List):-
maplist(triple_double,List,ListDouble),
check_no_dups(ListDouble).
check_no_dups([]).
check_no_dups([H|T]):-
H =X+Y,
maplist(dif(H),T),
H2 =Y+X,
maplist(dif(H2),T),
check_no_dups(T).
triple_double(X+Y+_Z,X+Y):-dif(X,Y).

Related

Prolog check if a or b exists in facts

I want to write a rule that allows me to check whether a movie or an actor exists in the given facts.
Example facts
starsin(ice_age,compton__doug).
starsin(hulk,wryn__rhiannon_leigh).
starsin(hulk,turman__john).
starsin(hook,mcnatt__adam).
Query Input
?- exists(ice_age).
true.
?- exists(turman_john).
true
?- exists(bob).
false
Rule written
exists(Movie,Actor) :- starsin(Movie,Actor).
My problem is my rule is that it doesn't check movie or actor. I am forced to type in the query:
?- exists(hulk,Y) or exists(X,turman_john)
I want to check movie or actor.Help.
You can simply check if the item you query on can be unified with the first or second parameter of one of the starsin/2 facts:
exists(Movie) :-
starsin(Movie, _).
exists(Actor) :-
starsin(_, Actor).
Here the underscore _ is a variable that we do not care about.

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 (Sicstus) - nonmember and setof issues

Given following facts:
route(TubeLine, ListOfStations).
route(green, [a,b,c,d,e,f]).
route(blue, [g,b,c,h,i,j]).
...
I am required to find all the pairs of tube Lines that do not have any stations in common, producing the following:
| ?- disjointed_lines(Ls).
Ls = [(yellow,blue),(yellow,green),(yellow,red),(yellow,silver)] ? ;
no
I came up with the below answer, however it does not only give me incorrect answer, but it also does not apply my X^ condition - i.e. it still prints results per member of Stations lists separately:
disjointed_lines(Ls) :-
route(W, Stations1),
route(Z, Stations2),
setof(
(W,Z),X^
(member(X, Stations1),nonmember(X, Stations2)),
Ls).
This is the output that the definition produces:
| ?- disjointed_lines(L).
L = [(green,green)] ? ;
L = [(green,blue)] ? ;
L = [(green,silver)] ? ;
...
I believe that my logic relating to membership is incorrect, however I cannot figure out what is wrong. Can anyone see where am I failing?
I also read Learn Prolog Now chapter 11 on results gathering as suggested here, however it seems that I am still unable to use the ^ operator correctly. Any help would be appreciated!
UPDATE:
As suggested by user CapelliC, I changed the code into the following:
disjointed_lines(Ls) :-
setof(
(W,Z),(Stations1, Stations2)^
((route(W, Stations1),
route(Z, Stations2),notMembers(Stations1,Stations2))),
Ls).
notMembers([],_).
notMembers([H|T],L):- notMembers(T,L), nonmember(H,L).
The following, however, gives me duplicates of (X,Y) and (Y,X), but the next step will be to remove those in a separate rule. Thank you for the help!
I think you should put route/2 calls inside setof' goal, and express disjointness more clearly, so you can test it separately. About the ^ operator, it requests a variable to be universally quantified in goal scope. Maybe a concise explanation like that found at bagof/3 manual page will help...
disjointed_lines(Ls) :-
setof((W,Z), Stations1^Stations2^(
route(W, Stations1),
route(Z, Stations2),
disjoint(Stations1, Stations2)
), Ls).
disjoint(Stations1, Stations2) :-
... % could be easy as intersection(Stations1, Stations2, [])
% or something more efficient: early fail at first shared 'station'
setof/3 is easier to use if you create an auxiliary predicate that expresses the relationship you are interested in:
disjoint_routes(W, Z) :-
route(W, Stations1),
route(Z, Stations2),
disjoint(Stations1, Stations2).
With this, the definition of disjointed_lines/1 becomes shorter and simpler and no longer needs any ^ operators:
disjointed_lines(Ls) :-
setof((W, Z), disjoint_routes(W, Z), Ls).
The variables you don't want in the result of setof/3 are automatically hidden inside the auxiliary predicate definition.

Prolog Predicates

So I have this knowledge base
family(person(name(tom),surname(fox),date(7,may,1950),job(bbc,15200)),
person(name(ann),surname(fox),date(9,may,1951),job(unemployed)),
children[person(name(pat),surname(fox),date(5,may,1973),job(unemployed)),
person(name(jim),surname(fox),date(5,may,1973),job(unemployed))]).
family(person(name(john),surname(doe),date(7,july,1957),job(carpenter,12300)),
person(name(lucy),surname(doe),date(19,february,1969),job(clerk,13500)),
children[person(name(mary),surname(doe),date(17,january,1990),job(unemployed)),
person(name(mat),surname(doe),date(4,may,1991),job(unemployed)),
person(name(george),surname(doe),date(5,august,1993),job(unemployed))]).
family(person(name(nick),surname(brown),date(14,may,1955),job(trucker,16300)),
person(name(carmen),surname(brown),date(25,april,1957),job(unemployed)),
children[person(name(david),surname(brown),date(15,august,1977),job(unemployed)),
person(name(james),surname(brown),date(7,october,1980),job(unemployed))]).
family(person(name(daniel),surname(sturgess),date(19,november,1956),job(unemployed)),
person(name(susan),surname(sturgess),date(18,october,1957),job(manager,15400)),
children[person(name(mick),surname(sturgess),date(5,december,1982),job(unemployed))]).
And what I'm trying to do is figure out a way to create a predicate wife/1 that will return all the working wives, and a predicate exists/1 that will return all the unemployed parents that are born before 1963.
I'm quite new to Prolog (using the swi environment) so any help is welcome!
Your database is in unusual format, since in Prolog attributes are normally associated to positions. Since you name all attributes, some flexibility is allowed. Assuming a 'working wife' it's the second entry of family/2 having a job \= unemployed at fixed (last) position:
wife(W) :- family(_,W,_), W = person(_,_,_,job(Job)), Job \= unemployed.
to get all:
?- findall(W, wife(W), Wives).
if job/1 attribute can appear in some other argument position, we can generalize the above clause:
wife(W) :- family(_,W,_), W =.. [person|Attrs], memberchk(job(Job), Attrs), Job \= unemployed.
exists/1 can be handled in similar way...
edit
correction to match job attribute: it appears with 1 or 2 arguments: assuming when a person is employed, s(he) will always have a salary, we can simplify matching job/2:
wife(W) :- family(_,W,_), W = person(_,_,_,job(_,_)).

Prolog dict predicate matching

Given this program, why am I forced to define every atom in the predicate, even if they're anonymous. Why is it that undefined variables in a dict predicate aren't thought of as anonymous?
funt2(X) :-
X = point{x:5, y:6}.
evalfunt(point{x:5, y : 6}) :-
write('hello world!').
evalfunt(point{x:_, y : _} ) :-
write('GoodBye world!').
Why can't I just say
evalfunt(point{x:5}) :-
write('GoodBye world!').
^that won't match, by the way.
I may as well just use a structure if I have to define every possible value in the dict to use dicts.
What's the motivation here? Can I do something to make my predicate terse? I'm trying to define a dict with 30 variables and this is a huge roadblock. It's going to increase my program size by a magnitude if I'm forced to define each variables (anonymous or not).
Dict is just a complex data type, like tuple, which has data AND structure. If you have, for example two facts:
fact(point{x:5, y:6}).
fact(point{x:5}).
Then the query
fact(point{x:_}).
will match the second one, but not the first one.
And the query
fact(point{x:_, y:_}).
Will match the first one, but not the second.
Now, if you want to match facts of the form fact(point{x:_, y:_, z:_}) only by one specific field, you can always write a helper rule:
matchByX(X, P) :- fact(P), P=point{x:X, y:_, z:_}.
So having facts:
fact(point{x:5, y:6, z:1}).
fact(point{x:1, y:2, z:3}).
fact(point{x:2, y:65, z:4}).
and quering
matchByX(1, P).
will return:
P = point{x:1, y:2, z:3}
UPDATE:
Moreover, in SWI-Prolog 7 version the field names can be matched as well, so it can be written in much more generic way, even for facts with different structures:
fact(point{x:5, y:6, z:1}).
fact(point{x:1, y:2}).
fact(point{x:2}).
fact(point{x:2, y:2}).
matchByField(F, X, P) :- fact(P), P.F = X.
So query:
?- matchByField(x, 2, P).
P = point{x:2} ;
P = point{x:2, y:2}.
I was able to accomplish what I needed by doing the following
checkiffive(Y) :-
get_dict(x, Y, V), V=5.
You need to use the built in methods for unifying values from a dict.
Described in chapter 5.4 of the SWI prolog reference
http://www.swi-prolog.org/download/devel/doc/SWI-Prolog-7.1.16.pdf

Resources