Prolog how to use if / else in this way? - if-statement

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.

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.

Repeating code in an if qualifier in Stata

In Stata I am trying to repeat code inside an if qualifier using perhaps a forvalues loop. My code looks something like this:
gen y=0
replace y=1 if x_1==1 & x_2==1 & x_3==1 & x_4==1
Instead of writing the & x_i==1 statement every time for each variable, I want to do it using a loop, something like this:
gen y=0
replace y=1 if forvalues i=1/4{x_`i'==1 &}
LATER EDIT:
Would it be possible to create a local in the line of this with the elements added together:
forvalues i=1/4{
local text_`i' "x_`i'==1 &"
display "`text_`i''"
}
And then call it at the if qualifier ?
Although you use the term "if statement" all your code is phrased in terms of if qualifiers, which aren't commands or statements. (Your use of the term "statement" is looser than customary, but that doesn't affect an answer directly.)
You can't insert loops in if qualifiers.
See for the differences
help if
help ifcmd
The entire example
gen y = 0
replace y = 1 if x==1 | x==2 | x==3 | x==4
would be better as
gen y = inlist(x, 1, 2, 3, 4)
or (dependent possibly on whatever values are allowed)
gen y = inrange(x, 1, 4)
A loop solution could be
gen y = 0
quietly forval i = 1/4 {
replace y = 1 if x == `i'
}
We can't discuss whether inlist() or inrange() would or would not be a solution for your real problem if you don't show to us.
I usually don't like - in Nick's terms - to write code to write code. I see an immediate, though not elegant nor 'heterodox', solution to your issue. The whole thing amounts to generate an indicator function for all your indicators, and use it with your if qualifier.
Implicit assumptions, which make this a bad, non-generalizable solution, are: 1) all variables are dummies, and you need them to be == 1, and 2) variable names are conveniently ordered 1 to N (although, if that is not the case, you can easily change the forv into a 'foreach var of varlist etc.')
g touse = 1
forv i =1/30{
replace touse = touse * x_'i'
}
<your action> if touse == 1

Recursively Assigning One List Element to Another in Prolog

This is a homework assignment and my first experience with Prolog. My goal is to create a list of Assignments from a list of people and a list of tasks. If a person has the letter identifier which matches the tasks then that persons ID and the Tasks ID are matched up and placed in a list of Assignments. My function prints out a list but it does not look like it is comparing all the elements. A sample input: schedule([p1,p2,p3],[t1,t2],Result). A sample output would look like [[p1,t1],[p2,t2][p3,t1],[p3,t2]].
What I have so far:
%%
%% person(ID, TASK_CAPABILITIES, AVAILABLE_HOURS)
%%
%% How many hours each person has available and what classes of tasks they
%% are capable of performing.
%%
person(p1, [c,a], 20).
person(p2, [b], 10).
person(p3, [a,b], 15).
person(p4, [c], 30).
%%
%% task(ID, REQUIRED_HOURS, TASK_CLASS)
%%
%% How long each task requires and what class it falls under.
%%
task(t1, a, 5).
task(t2, b, 10).
task(t3, c, 15).
task(t4, c, 10).
task(t5, a, 15).
task(t6, b, 10).
%test arithmetic functions
add(X, Y, Z) :- Z is X + Y.
subtract(X,Y,Z) :- Z is X - Y.
schedule([],[],[]).
schedule(People,
[Task|OtherTasks],
[[PersonId, TaskId]|RestOfAssignments]):-
member(PersonId, People),
person(PersonId, PersonCapabilities,_),
member(TaskId, [Task|OtherTasks]),
task(TaskId, TaskType,_),
member(TaskType, PersonCapabilities),
schedule( _, OtherTasks, RestOfAssignments).
My reasoning behind what I wrote was that the list of People would be compared to each task, then that task would be replaced by the next task and the comparison would repeat. What I see in the trace of this function instead is that the tasks are being removed from the list but are only compared to the first two People. My question is how can I get the schedule function to check the full list of people for each task?
Your problem seems ill specified, and you are simplifying too much... the code should keep into account hours availability as well as memberships. Ignoring this problem, select/3 instead of member/2 could help to model a naive solution:
schedule([],_,[]).
% peek a suitable task for PersonId
schedule([PersonId|People], Tasks, [[PersonId, TaskId]|RestOfAssignments]):-
select(TaskId, Tasks, RestTasks),
person(PersonId, PersonCapabilities,_),
task(TaskId, TaskType,_),
memberchk(TaskType, PersonCapabilities),
schedule(People, RestTasks, RestOfAssignments).
% if no suitable task for PersonId
schedule([_PersonId|People], Tasks, Assignments):-
schedule(People, Tasks, Assignments).
yields these solutions
?- schedule([p1,p2,p3],[t1,t2],Result).
Result = [[p1, t1], [p2, t2]] ;
Result = [[p1, t1], [p3, t2]] ;
Result = [[p1, t1]] ;
Result = [[p2, t2], [p3, t1]] ;
Result = [[p2, t2]] ;
Result = [[p3, t1]] ;
Result = [[p3, t2]] ;
Result = [].

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).

Regular expression puzzle

This is not homework, but an old exam question. I am curious to see the answer.
We are given an alphabet S={0,1,2,3,4,5,6,7,8,9,+}. Define the language L as the set of strings w from this alphabet such that w is in L if:
a) w is a number such as 42 or w is the (finite) sum of numbers such as 34 + 16 or 34 + 2 + 10
and
b) The number represented by w is divisible by 3.
Write a regular expression (and a DFA) for L.
This should work:
^(?:0|(?:(?:[369]|[147](?:0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[147]0*(?:\+?(?:0\
+)*[369]0*)*\+?(?:0\+)*[258])*(?:0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[258]|0*(?:
\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[147]0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[147])|[
258](?:0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[258]0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0
\+)*[147])*(?:0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[147]|0*(?:\+?(?:0\+)*[369]0*)
*\+?(?:0\+)*[258]0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[258]))0*)+)(?:\+(?:0|(?:(?
:[369]|[147](?:0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[147]0*(?:\+?(?:0\+)*[369]0*)
*\+?(?:0\+)*[258])*(?:0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[258]|0*(?:\+?(?:0\+)*
[369]0*)*\+?(?:0\+)*[147]0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[147])|[258](?:0*(?
:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[258]0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[147])*
(?:0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[147]|0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)
*[258]0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*[258]))0*)+))*$
It works by having three states representing the sum of the digits so far modulo 3. It disallows leading zeros on numbers, and plus signs at the start and end of the string, as well as two consecutive plus signs.
Generation of regular expression and test bed:
a = r'0*(?:\+?(?:0\+)*[369]0*)*\+?(?:0\+)*'
b = r'a[147]'
c = r'a[258]'
r1 = '[369]|[147](?:bc)*(?:c|bb)|[258](?:cb)*(?:b|cc)'
r2 = '(?:0|(?:(?:' + r1 + ')0*)+)'
r3 = '^' + r2 + r'(?:\+' + r2 + ')*$'
r = r3.replace('b', b).replace('c', c).replace('a', a)
print r
# Test on 10000 examples.
import random, re
random.seed(1)
r = re.compile(r)
for _ in range(10000):
x = ''.join(random.choice('0123456789+') for j in range(random.randint(1,50)))
if re.search(r'(?:\+|^)(?:\+|0[0-9])|\+$', x):
valid = False
else:
valid = eval(x) % 3 == 0
result = re.match(r, x) is not None
if result != valid:
print 'Failed for ' + x
Note that my memory of DFA syntax is woefully out of date, so my answer is undoubtedly a little broken. Hopefully this gives you a general idea. I've chosen to ignore + completely. As AmirW states, abc+def and abcdef are the same for divisibility purposes.
Accept state is C.
A=1,4,7,BB,AC,CA
B=2,5,8,AA,BC,CB
C=0,3,6,9,AB,BA,CC
Notice that the above language uses all 9 possible ABC pairings. It will always end at either A,B,or C, and the fact that every variable use is paired means that each iteration of processing will shorten the string of variables.
Example:
1490 = AACC = BCC = BC = B (Fail)
1491 = AACA = BCA = BA = C (Success)
Not a full solution, just an idea:
(B) alone: The "plus" signs don't matter here. abc + def is the same as abcdef for the sake of divisibility by 3. For the latter case, there is a regexp here: http://blog.vkistudios.com/index.cfm/2008/12/30/Regular-Expression-to-determine-if-a-base-10-number-is-divisible-by-3
to combine this with requirement (A), we can take the solution of (B) and modify it:
First read character must be in 0..9 (not a plus)
Input must not end with a plus, so: Duplicate each state (will use S for the original state and S' for the duplicate to distinguish between them). If we're in state S and we read a plus we'll move to S'.
When reading a number we'll go to the new state as if we were in S. S' states cannot accept (another) plus.
Also, S' is not "accept state" even if S is. (because input must not end with a plus).