0

我正在编写一个顾问程序,为学生选择下学期的课程。其中一项任务是检查学生是否具备所有先决条件。到目前为止,我有:

hasPrereqs(Student, Class) :-
    (prereq(Pre, Class) -> 
        hasClass(Student, Pre);
    true).

hasClass(Student, Class) :-
    (creditFor(Student, Class);
    currentlyTaking(Student, Class)).

prereqs 声明如下:

prereq(csc140, csc145).
prereq(csc140, csc180).
prereq(csc140, csc198).
prereq([csc140, csc130], csc201).
prereq(csc145, csc201).

这适用于所有可能性,除非另一个课程需要两个课程(例如csc201,学生可以参加 csc140 和 csc130 或仅 csc140)。我认为列表是要走的路,但我不知道如何实现它。

我尝试创建另一个hasPrereqs如下:

hasPrereqs(Student, Class) :-
forall(prereq(Pre, Class),
(compound(Pre) -> 
    hasClass(Student, Pre)).

这个不起作用,因为Pre它是一个列表,而不是一个直接的原子,因此:

creditFor(somekittens, csc130).
creditFor(somekittens, csc140).
/* Returns false, because I don't have credit for the list, just the two classes */
creditFor(somekittens, [csc130, csc140]).

如何设置系统以便需要其他几个课程的课程正常工作?

4

3 回答 3

1

我会这样做:

hasPrereqs(Student, Class) :-
  prereq(Class, Pres),
  forall(member(Pre, Pres), hasClass(Student, Pre)).

hasClass(Student, Class) :-
  (creditFor(Student, Class);
  currentlyTaking(Student, Class)).

prereq(csc140, []).
prereq(csc145, [csc140]).
prereq(csc180, [csc140]).
prereq(csc198, [csc140]).
prereq(csc201, [csc140, csc130]).
prereq(csc201, [csc145]).

我交换了参数的顺序,prereq/2并将先决条件列成一个列表,不管有多少。这使得代码使用prereq/2更加一致,并为没有先决条件的类提供了一个符号,即prereq(foo, []).

然后我使用forall/2member/2确保学生满足所有先决条件。

于 2012-10-26T23:44:00.343 回答
0

任何有函数式编程背景的人(比如我!)都想这样做:

foreach(var i=0;i<Class.length;i++) {
    if(!hasClass(Student, Class[i])) {
         return false;
    }
}
return true;

不幸的是,这不起作用。没有简单的方法(据我所知)遍历 Prolog 中的列表并返回trueIFF 所有元素都返回 true。相反,使用递归方法遍历列表。hasClass为这样添加另一个定义:

hasClass(Student, ClassList) :-
    /* Splits up the list into:
    H = the first element of the list as an atom
    T = The rest of the list elements as a list
    (if there's only one list element, T is equal to []) */
    [H|T] = ClassList,
    hasClass(Student, H),
    /* if T isn't equal to [], recursively check the rest of the list's elements */
    (T \= [] -> hasClass(Student, T);true).
于 2012-10-26T00:06:41.940 回答
0
hasPrereqs(Student, Class) :-
    (prereq(Pre, Class) -> 
        hasClass(Student, Pre);
    true).

无论变量实例化和您拥有的实际事实数据如何,它始终是正确的。唯一的目的可能是 prereq/2 或 hasClass/2 执行的一些副作用(如 IO 或 DB 修改),而这似乎不是。

现在来回答。SWI-prolog 有is_list /1 和maplist /2,后者可以对每个元素重复相同的测试,只有当测试对所有元素都成功时才为真:

编辑类本来是Pre

hasPrereqs(Student, Class) :-
 forall(prereq(Pre, Class),
   (   is_list(Pre)
   ->  maplist(hasClass(Student), Pre)
   ;   hasClass(Student, Pre)
   )).

或者更好,如果您打算仅将 hasClass 重用于测试,并假设一个已经在工作的 hasClass/2

hasPrereqs(Student, Class) :-
   forall(prereq(Pre, Class), hasClass(Student, Pre)).

hasClass(Student, Classes) :-
   is_list(Classes) -> maplist(hasClass(Student), Classes).

编辑以下是错误的,在最后一次通话时循环...

还有一种方式。在您的回答中,您接近解决方案:

hasClass(Student, ClassList) :-
    (   [H|T] = ClassList
    ->  hasClass(Student, H), hasClass(Student, T)
    ;   hasClass(Student, ClassList)
    ).

编辑解决方案应该更简单:

hasClass(Student, [Class|ClassList]) :-
    hasClass(Student, Class),
    !, hasClass(Student, ClassList).
hasClass(_Student, []).
于 2012-10-26T08:31:33.147 回答