Visual Prolog 5.2 The variable is not bound in this clause - prolog

I have "in" predicate that goes through "people". But the X doesnt get a "person" and returns false, as i understood. Test goal and Run throw the same error
domains
person = c(name, nationality)
people = person*
name, nationality = symbol
predicates
name(person, name)
nationality (person, nationality)
in(person, people)
solve
clauses
name(c(N,_), N).
nationality (c(_,T), T).
in(X,[X,_,_,_]). %Error here
in(X,[_,X,_,_]).
in(X,[_,_,X,_]).
in(X,[_,_,_,X]).
solve :- in(C1, People), name(C1, brown),
in(C2, People), name(C2, clemens),
in(C3, People), name(C3, grifit),
in(C4, People), name(C4, grin),
in(C5, People), nationality(C5, canadian),
in(C6, People), nationality(C6, american),
in(C7, People), nationality(C7, australian),
in(C8, People), nationality(C8, british),
not(nationality(C1, british)),
not(nationality(C2, british)),
not(nationality(C3, american)),
not(nationality(C3, australian)),
not(nationality(C1, australian)),
not(nationality(C4, australian)),
write(People), nl.
goal
solve.
I expect X to be bound to the person (people item). What am I doing wrong?

Related

Sort a list in Prolog

The fact base stores exam schedule information in the form: exam ('group', 'subject', 'teacher', date). Write a program that allows you to select exam information for the group / teacher, sorting it by date.
So, I already know how to get data from base depending on what you want (by Group number or by teacher name):
exam('426-1', 'PROGRAMMING', 'John', 08-12-2019).
exam('426-1', 'MATH', 'John', 18-12-2019).
exam('426-3', 'ENG', 'Gabe', 16-12-2019).
exam('426-4', 'LITERATURE', 'Homer', 11-12-2019).
findByGroup(Number, Exams) :-
findall([A,B|C], exam(Number, A,B,C), Exams).
findByTeacher(Name, Exams) :-
findall([A,B|C], exam(A, B, Name,C), Exams).
Here's the input
findByGroup('426-1', X).
And here's the output
X = [['PROGRAMMING', 'John'|8-12-2019], ['MATH', 'John'|18-12-2019]]
But I have no idea how should I sort my output by date. For example, how can I sort the data by ascending of the date?
Example:
Before X = [['MATH', 'John'|18-12-2019], ['PROGRAMMING', 'John'|8-12-2019]]
After X = [['PROGRAMMING', 'John'|8-12-2019], ['MATH', 'John'|18-12-2019]]
There are a couple of things that need to be considered:
The date format needs to be in a format that sorts chronologically
The information for each exam item needs to be organized into a term that will sort based upon the date
For the date format, you are using a - functor with integer arguments. This is Ok. To make it sortable chronologically, the order needs to be year-month-day. You can either change your original data to be in that form, or translate to the sortable form before you do your sorting.
Regarding the exam data record, if you create a record formatted as a term with the date as the first argument, then sorting the records will sort by date. For example, any of the following terms will sort by date in Prolog:
exam(Date, Number, A, B)
[Date, Number, A, B]
...etc...
Now we apply this to your problem. Sorting can be done using setof/3 instead of findall/3 if your date is formatted in a sortable form:
% exam(Number, Class, Name, Date)
exam('426-1', 'PROGRAMMING', 'John', 2019-12-08).
exam('426-1', 'MATH', 'John', 2019-12-18).
exam('426-3', 'ENG', 'Gabe', 2019-12-16).
exam('426-4', 'LITERATURE', 'Homer', 2019-12-11).
findByGroup(Number, Exams) :-
setof([Date, Number, Class, Name], exam(Number, Class, Name, Date), Exams).
findByTeacher(Name, Exams) :-
setof([Date, Number, Class, Name], exam(Number, Class, Name, Date), Exams).
This will provide terms with attributes in a slightly different order: [Date, Number, Class, Name]. You can either just work with that, or you could easily change it:
reformat_exam_list([Y-M-D, Number, Class, Name], [Number, Class, Name, D-M-Y]).
And then call maplist(reformat_exam_list, Exams, ReformattedExams).
If you can't change the date format in your original facts, you can pre-process them as part of your setof/3 call.
reformat_sorted_exam(exam(Y-M-D, Number, Class, Name), exam(Number, Class, Name, D-M-Y)).
findByGroup(Number, Exams) :-
setof(exam(Y-M-D, Number, Class, Name), exam(Number, Class, Name, D-M-Y), ExamList),
maplist(reformat_sorted_exam, SortedExams, Exams).
Here I also used an exam structure instead of a list to represent an exam. You get the idea...

Is there a way to reach the same result in a clause using less variables?

I am trying to find/understand another way to reach a result by calling more predicates in a clause when using less free variables.
Coming from this problematic How to correctly filter a clause that returns multiple duplicated values? I wanted to try reaching to the same result adding a little difficulty.
Facts:
home(peter, sanFrancisco, 1000).
home(ash, sanFrancisco, 100).
home(juan, sanFrancisco, 400).
home(juan, california, 700).
home(ash, california, 600).
home(peter, california, 500).
home(peter, vegas, 100).
home(ash, vegas, 80).
home(juan, vegas, 60).
townHomesTotalCost(sanFrancisco, 1500).
townHomesTotalCost(california, 1800).
townHomesTotalCost(vegas, 240).
Answer provided in the previous question was very helpful and I wanted to try something different to understand better logic programming. Lets say that instead of the home price, I want to compare by percentage. For the percentage I created this clause:
townHomeCostByPercentage(Name, Town, Percentage):-
home(Name, Town, Price),
townHomesTotalCost(Town, Total),
Percentage is round(((Price * 100) / Total)).
Getting the most expensive home in a town is such that no other home in that town is more expensive than it (Lets say I don't want to use the Price as an entry variable):
most_expensive(Name, Town):-
home( Name, Town, Price),
not((home( _, Town, P), P > Price)).
The second most expensive home in a town is such that is the most expensive among the homes which are not the most expensive home in that town:
second_most_expensive(Name, Town):-
most_expensive(Name, Town),
home(Name, Town, Price),
home(_, Town, Price2), Price < TopPrice,
not((home(_, Town, P), P < TopPrice, P > Price)).
I can't get why this is getting the same result as most_expensive(Name, Town)
And finally, using the percentage, return the Name but comparing percentage in that the most_expensive is < than the second_most_expensive + 20:
top_house_owner(Name) :-
most_expensive(Name, T),
townHomeCostByPercentage(Name, T, TopPercentage),
second_most_expensive(_, T),
townHomeCostByPercentage(_, T, Percentage),
TopPercentage < Percentage + 20.
This also returning the results name of most_expensive(Name, Town) and can't get why.

Twins in family database

Here is my family database. But I am unable to find out twin babies from this database. I am given this question: Define the relation twins(Child1, Child2) to find twins in the family database.
family(
person(tom,right,date(17,may,1950),works(mathematician)),
person(ann,right,date(29,may,1951),unemployed),
[
person(pat,right,date(5,may,1983),unemployed),
person(max,right,date(5,may,1983),unemployed)
]).
family(
person(nick,wellbard,date(15,september,1954),works(electrician)),
person(cathrine,wellbard,date(11,march,1970),unemployed),
[
person(john,wellbard,date(15,may,1983),works(musician)),
person(mike,wellbard,date(25,may,1989),unemployed),
person(chloe,wellbard,date(15,may,1983),unemployed)
]).
family(
person(john,brock,date(17,january,1978),unemployed),
person(mary,brock,date(19,march,1951),works(teacher)),
[
person(tony,brock,date(20,may,1975),unemployed),
person(sasha,brock,date(1,april,1979),unemployed),
person(josh,brock,date(29,april,1983),unemployed)
]).
family(
person(abc,xyz,date(17,may,1950),works(mathematician)),
person(def,xyz,date(29,may,1951),unemployed),
[]).
Any help will be appreciated.
Well,it's simple to control that you have same family name and also same date of birth.
in_list(person(Name,F,date(D,M,Y),W),[person(Name,F,date(D,M,Y),W)|_T]):-
!.
in_list(X,[_|T]):-
in_list(X,T).
twins(person(Nam,F,Date,W),person(Name,F,Date,We)):-
family(person(_,F,_,_),person(_,F,_,_),T),
in_list(person(Nam,F,Date,W),T),
in_list(person(Name,F,Date,We),T).

How to findall for universal facts in prolog?

In Prolog I can write
child(martha,charlotte).
child(charlotte,caroline).
child(caroline,laura).
child(laura,rose).
descend(X,Y) :-
child(X,Y).
descend(X,Y) :-
child(X,Z),
descend(Z,Y).
And then write
?- findall(X,descend(martha,X),Z).
and get four solutions
Z = [charlotte,caroline,laura,rose]
But if I then add an universal fact
likes(X,pomegranate).
and try
?- findall(X,likes(X, pomegranate),Z).
I get:
Z = [_G17].
What is that _G17?
What do I need to change to get essentially all variables? ( since likes(X,pomegranate) should mean everything likes pomegranate... right?) :
Z = [martha,charlotte,caroline,laura,rose]
Two solutions. The clean solution is to have a table that lists all "things" in the universe you describe:
person(martha).
person(charlotte).
% etc
And then your "likes" would be rather:
person_likes(P, pomegranate) :-
person(P).
You can also try to hack around it:
person(P) :- child(P, _).
person(P) :- child(_, P).
But this is... unsatisfactory? Think about a relational database: you would have two tables:
CREATE TABLE person (
id INTEGER PRIMARY KEY, -- usually autogenerated
name TEXT NOT NULL
);
CREATE TABLE parent_child (
parent_id INTEGER NOT NULL,
child_id INTEGER NOT NULL,
FOREIGN KEY parent_id REFERENCES person(id),
FOREIGN KEY child_id REFERENCES person(id)
);
The only reason, as far as I am aware, why you don't do the exact same thing in Prolog, is that most introductory tutorials are trying to seduce you and avoid going into such details. And of course the Prolog "database" is not a true relational database (for example, argument position does matter!).
TL;DR You can't avoid thinking about Prolog's resolution strategy when using Prolog.

Prolog queries beginner

Given the predicates
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
how would you find someone who has the oldest account at the same bank using only the following "- , < =< > >= not" operators? I'm deeply lost!
Based on your comment, \+ is used for not (not proveable):
created(Who,Somebank,Month1,Year1),
\+ ( created(_,Somebank,Month2,Year2), older(Month2,Year2,Month1,Year1) )
with older/4 defined as:
older(_Month2,Year2,_Month1,Year1) :- Year2 < Year1.
older(Month2,Year,Month1,Year) :- Month2 < Month1.

Resources