Prolog - sum of list members - list

I have been trying to find the circumference of a country in Prolog.
I have the finished predicate
borders(Country1, Country2, Length)
and
setof(Item, Condition, Set)
which gives a list of all items into the set which fulfill the condition.
To get the circumference I tried doing this:
circumference(C, Country) :-
setof(X, borders(Country,_,X), Set),
sum_list(Set,C).
sum_list([], 0).
sum_list([H|T], C) :-
sum_list(T, Rest),
C is H + Rest.
... but the output I'm getting is only the length between two countries in the border predicate.
My test:
?– circumference(C,angola).
C = 201 ;
C = 1376 ;
C = 2511 ;
C = 1110.
rules:
borders(angola,namibia,1376).
borders(angola,congo,201).
borders(angola,zambia,1110).
borders(angola,zaire,2511).
Why doesn't C become the sum of these numbers?

I swapped circumference/2 arguments, but you get the idea...
circumference(Country,C) :- aggregate(sum(Y),S^borders(Country,S,Y),C).

The problem has to do with the variable binding for the other country. At first it looks crazy:
?- setof(X, borders(Country,_,X), Set).
Country = angola,
Set = [201] ;
Country = angola,
Set = [1376] ;
Country = angola,
Set = [2511] ;
Country = angola,
Set = [1110].
But if you name that variable it becomes clear what's going on:
?- setof(X, borders(Country,OtherCountry,X), Set).
Country = angola,
OtherCountry = congo,
Set = [201] ;
Country = angola,
OtherCountry = namibia,
Set = [1376] ;
Country = angola,
OtherCountry = zaire,
Set = [2511] ;
Country = angola,
OtherCountry = zambia,
Set = [1110].
It can't group them together because the other country differs, even if you're not interested in the value that binding received. The right solution with setof/3 is to make OtherCountry existentially quantified, which makes it not matter for the purposes of the grouping:
?- setof(X, OtherCountry^borders(Country,OtherCountry,X), Set).
Country = angola,
Set = [201, 1110, 1376, 2511].
In setof/3 and bagof/3, existential quantification is a way of saying that OtherCountry will take on various values but you're not interested in them for the purposes of the grouping. There are lots of scenarios where findall/3 will only produce a single result but you may want separate groupings based on some of the other variables in the Goal parameter, so I think it's good to know about this technique even though findall/3 is probably a more straightforward solution in this case. More discussion can be found on this answer.

Related

Parsing list of items with DCG?

How do you parse string which is list of items f.e.
1,2,3,4
aa,b,ccc,ddd
Currently I do this (and is the comma) :
sub(X,Y) --> req(X), [and], req(Y).
sub(X) --> req(X).
req(on(X,Y)) --> [X,is,on,the,Y],!.
req(on(X,Y)) --> [X,is,on,Y],!.
req(on(X,Y)) --> [X,on,Y],!.
req(isa(X,Y)) --> [X,is,a,Y].
?- phrase(sub(T,U),[box,is,on,the,table, and, box,is,a,object], R).
T = on(box, table),
U = isa(box, object),
R = [].
but in this case sub() have two different heads sub(X) and sub(X,Y) , which messes the higher level.
I did this :
sub([X|Y]) --> req(X), [and], sub(Y).
sub([X]) --> req(X).
which seems to work ... but returns too many results ..??
?- phrase(sub(T),[box,is,on,the,table, and, box,is,a,object, and, apple , is,a,fruit], R).
T = [on(box, table), isa(box, object), isa(apple, fruit)],
R = [] ;
T = [on(box, table), isa(box, object)],
R = [and, apple, is, a, fruit] ;
T = [on(box, table)],
R = [and, box, is, a, object, and, apple, is, a|...].
it should stop at the first results ...!!
ooo i have to reverse the order, right ?
sub([X]) --> req(X).
sub([X|Y]) --> req(X), [and], sub(Y).
With your last definition:
sub([X]) --> req(X).
sub([X|Y]) --> req(X), [and], sub(Y).
The following queries work:
?- phrase(sub(Sub), [box,is,on,the,table, and, box,is,a,object]).
Sub = [on(box, table), isa(box, object)] ;
false.
?- phrase(sub(Sub), [box,is,on,the,table, and, box,is,a,object, and, apple,is,a,fruit]).
Sub = [on(box, table), isa(box, object), isa(apple, fruit)] ;
false.
Note that I only give two arguments to phrase. phrase has a form with two arguments, and one with three arguments. As a beginner, you will almost certainly only want to use the two-argument form. The two-argument form parses the entire list you give it and succeeds only if really everything in the list could be parsed. This is almost certainly what you want as a beginner. The three-argument form allows you to parse only the beginning of a list, then get back the suffix that wasn't parsed. You are not advanced enough to need this.

Prolog how to use if / else in this way?

In Prolog, i want to use an if-else statement in the following way:
lecture(spanish,monday,at8).
lecture(french,monday,at8).
lecture(english,monday,at10).
lecture(german,tuesday,at8).
generatePossibleSchedulesWithTwoLectures(LectureA,LectureB):-
lecture(LectureA,A,X),
lecture(LectureB,B,Y),
%If A of LectureA is equal to B of lectureB -> times must be un-equal
% How to do implement the requirement of the line above here?
As a result, the function should generate schedules with two lectures which are not in the same time slot.
You started by assuming, i want to use an if-else statement in the following way, but the logic of your problem doesn't describe an if-then-else condition. An if-then-else isn't the construct you want to achieve your goal of:
Generate schedules with two lectures which are not in the same time slot
Also, the Prolog if-then-else construct A -> B ; C removes the choice point you are after. For some detail on this, see the answer to, What's the meaning of Prolog operator '->'
What you really want is to say:
A schedule two lectures A & B is valid if Day1 and Time1 are a time slot for A, and Day2 and Time2 are a time slot for B, and time slots Day1-Time1 and Day2-Time2 are different.
If fact, to avoid duplicates and to keep the schedule in chronological order, we can enforce that the "first" time slot is earlier than the second. This translates to:
generate_possible_schedules_with_two_lectures(LectureA, LectureB):-
lecture(LectureA, Day1, Time1),
lecture(LectureB, Day2, Time2),
dif(LectureA, LectureB), % don't schedule the same class
Day1-Time1 #< Day2-Time2 % timeslots in order, and different
We simplify the time slot comparison by forming a term A-B with the day and time. You could do the longer comparison, comparing the day and tie individually, replacing Day1-Time1 #< Day2-Time2 with (Day1 #< Day2 ; Day1 == Day2, Time1 #< Time2). In either case, you get the following results with your data:
?- generate_possible_schedules_with_two_lectures(A, B).
A = spanish,
B = german ;
A = french,
B = german ;
A = english,
B = spanish ;
A = english,
B = french ;
A = english,
B = german ;
false.

How can I sort and include rules sorting in django?

I'm not to confident that I am asking this question correctly but this is what I'd like to do.
In django admin, I would like to write an action that sorts the list of my contestants randomly and doesn't allow two people with the same first name to be within 4 records of eachother. So basically,
if you have John L. John C. Carey J, Tracy M. Mary T., the records would be listed like this:
John L.
Mary T.
Carey J.
Tracy T.
John C.
OR
How can I write an action that would create random groups where two people with the same name wouldn't be within the same group like so:
John L. John C. Carey J, Tracy M. Mary T. =
Group 1
John L.
Mary T.
Carey J.
Tracy T.
Group 2
John C.
Forgive me if it isn't very clear, let me know and I'll try to specify further but any help would be appreciated
EDIT:
Is this what you are referring to? I can't quite figure out how to compare the fields to see if they are the same
Model:
class people(models.Model)
fname = model.CharField()
lname = model.CharField()
group = model.IntegerField()
View:
N = 4
Num = randint(0, N-1)
for x in queryset:
x.group = Num
if group == group| fname == fname | lname == lname:
x.group = (Num + 1) % N
Your first question cannot be solved always. Just think of all contestants have the same name, then you actually cannot find a solution to it.
For the second question, I can suggest an algorithm to do that, though.
Since I do not see any restriction on the number of groups, I will suggest a method to create the least number of groups here.
EDIT: I assumed you don't want 2 people with same "First name" in a group.
The steps are
Count the appearance of each name
count = {}
for x in queryset:
if x.fname not in count:
count[x.fname] = 0
count[f.name] += 1
Find the name with the most appearance
N = 0
for x in queryset:
if count[x.fname] > N:
N = count[x.fname]
Create N groups, where N equals to the number of appearance of the name in step 2
For each name, generate a random number X, where X < N.
Try to put the name into group X. If group X has that name already, set X = (X + 1) % N and retry, repeat until success. You will always find a group to put the contestant.
from random import randint
groups = [[]] * N
for item in queryset:
X = randint(0, N-1)
while item.fname in groups[X]:
X = (X + 1) % N
groups[X].append(item.fname)
item.group = X
EDIT:
Added details in steps 1, 2, 4.
From the code segment in your edited, I think you do not actually need a definition of "group" in model, as seems you only need a group number for it.

removing from the list Prolog

Hi everyone I'm newbie in prolog and I have such a list: (actually It is output of my predicate not a list )
P = [1/1, 1/3] ;
P = [1/1, 2/3] ;
P = [1/3, 1/1] ;
P = [1/3, 2/1] ;
P = [2/1, 1/3] ;
P = [2/1, 2/3] ;
P = [2/3, 1/1] ;
P = [2/3, 2/1] ;
and I need to remove dublicete terms.For example [1/1,2/3] and [2/3,1/1]is same and I should remove one of them , which one is not important ,How could I do that in prolog ?? Thanks in advance
NOTE I LEARNT THAT findALL should be good way for this but still dont know the answer please help me .
Unless you actually show us your code, it's never going to be possible to give you precise answers.
I assume you have a predicate f/1 such that:
?- f(P).
produces the interactive result you show above. A simple solution is to change your query:
?- f([X,Y]), X < Y.
This will produce the following result:
X = 1/3, Y = 1/1 ;
X = 1/3, Y = 2/1 ;
X = 2/3, Y = 1/1 ;
X = 2/3, Y = 2/1 ;
findall/3 isn't sufficient to solve this particular situation, because you've defined uniqueness in a way that ignores the position in the list. In Prolog (and everything else) [X,Y] and [Y,X] are not equal, so you'd have to find a trick to get this to give you "unique" results.

Prolog: Writing each element of an existing list

I'm trying to do two things. (1) display each element of an existing list, and (2) search a list to display all names that contain that element.
Here are some facts:
classes(hannes, [cs490, cs499, cs413]). % name has this list of classes
classes(tony, [ma330, ma211, ma250]).
classes(nicholas, [cs424, cs570, ma330]).
classes(pj, [ma211, ma250, ma285, cs424]).
classes(inga, [cs285, cs307, cs309]).
classes(christine, [ma285, ma211, ma330]).
classes(lisa, [cs424, cs413, cs490]).
classes(marty, [cs570, cs424]).
And, here is my rule so far:
taking(N,C) :- % student Name N is taking class C
classes(N,Cs),
[C|T] = Cs.
At the moment, I know this only takes the head of the list and displays it. I need to display each item of the list (one line at a time, if easy enough to do, but not important). But, I also need to be able to do it in reverse. If 1 course is queried (ma330), I want it to display all students that have that particular course.
Query example 1:
?- taking(nicholas, Classes).
Classes = [cs424, cs570, ma330] ;
OR
?- taking(nicholas, Classes).
Classes = cs424 ;
Classes = cs570 ;
Classes = ma330 ;
Query example 2:
?- taking(Names, ma330).
Names = tony ;
Names = nicholas ;
Names = christine ;
I'm going to keep searching for a resolution, but if anyone can help, it would be appreciated.
Thank you!!!
Think of that : C is member of Classes.
EDIT OK try this code :
taking(N,C) :- % student Name N is taking class C
classes(N,Cs),
member(C, Cs).