测试以查看list / variable的所有元素是否为true

时间:2012-10-26 00:06:42

标签: prolog

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

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

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

先决条件声明如下:

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

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

3 个答案:

答案 0 :(得分: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来确保学生满足所有先决条件。

答案 1 :(得分:0)

任何来自函数式编程背景的人(比如我!)都希望这样做:

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

不幸的是,这不起作用。在我的知识中没有简单的方法遍历Prolog中的列表并返回true IFF返回的所有元素都是真的。而是使用递归方法遍历列表。添加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).

答案 2 :(得分: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, []).