2
votes
course(cmput325).
course(cmput175).
course(cmput201).
course(cmput204).
prerequisite(cmput204, cmput325).
prerequisite(cmput175, cmput201).
prerequisite(cmput175, cmput204).

I need to write a new predicate, which is

can_take(+L,?C).

Definition:

L is a given list of courses that a student has already taken. If C is also given,then the predicate should check whether the student has all the required courses for C. If C is a variable, then with backtracking, the predicate should produce one course at a time that the student can take now. Courses can be in any order, but each course should be generated only once, and you should not return any courses that the student has already taken.

Example:

?- findall(C, can_take([cmput175], C), L).
should return

L = [cmput201, cmput204].

Here is my predicate:

can_take(L,C) :- prerequisite(L,C).
can_take([L|List],C) :- prerequisite(L,C),can_take(List,C).

This predicate didn't return the correct result, and it just returned false. I think it is because I didn't determine the condition when L is empty, however, if I tried to add L \== [] in either of them. It still gave me error...what should I do so that this predicate will stop and give me result?

-------update-------

pre(X,C) :- prerequisite(X,C).   
pre(X,C) :- prerequisite(X,Y), pre(Y,C).

pre2(C,L) :- findall(L1,pre(L1,C),L).

required(C,L) :- pre2(C,L1),sort(L1,L).

can_take([],_).
can_take(L,C) :- required(C,L).
can_take([L|List],C) :- prerequisite(L,C),can_take(List,C).

Here is my code test:

?- required(cmput325,L).
L = [cmput175, cmput204].

?- required(cmput204,L).
L = [cmput175].

?- can_take([cmput175],X).
X = cmput201 ;
X = cmput204 ;

?- findall(C, can_take([cmput175], C), L).
L = [cmput201, cmput204].


?- can_take([cmput204],cmput325).
false. (this one is OK)

?- can_take([cmput175,cmput204],cmput325).
true ;
false. (this one is OK)

?- can_take([cmput175],cmput204).
true ;
true ;
false.

the last one is not ok because I don't want it to return two true statements...so what I want is just let it stop when either second or last line returns true. For my assignment, I am not allowed to use cut operator !, is there any other way to do it?

1
When you add L \== [], you will make things worse as you add a further condition.false
@false: that would yield wrong results for the query findall(C, can_take([], C), L).gusbro
@gusbro: so is there a way to fix this problem?Code Vanessa
@gusbro: oh, wait, can_take([]._)actually worked, i added this fact instead of replacing my first fact.(I checked findall, it actually worked as well)Code Vanessa
@CodeVanessa: what are you getting with this query findall(C, can_take([], C), L). which would be asking which courses you can take when you have not taken any prior courses ?gusbro

1 Answers

3
votes

(I will assume that you can take a course a second time, even if you have taken it already. That at least are the rules I know of.)

You can take a course, provided you have taken all required courses already.

There is no direct "all" in Prolog. But you can formulate this differently

You can take a course, provided there is no required course that you have not taken already.

can_take(Takens, Next) :-
    course(Next),
    iwhen( ground(Takens), 
           \+ ( prerequisite(Required, Next), \+ member(Required, Takens) ) ).

This uses iwhen/2 to guard against cases where Takens is not fully instantiated.

Note that there is a slight difference to your examples:

?- findall(C, can_take([cmput175], C), L).
L = [cmput175, cmput201, cmput204].
%    ^^^^^^^^

Disclaimer
Your problem is inherently non-monotonic: By adding further facts for requirements you are reducing the number of possible courses you may take. As a beginner, rather stick to problems that are monotonic in nature. It is on this side where Prolog really excels.