【问题标题】:SWI-Prolog: How to stop the predicate when the list is empty? (predicate included)SWI-Prolog:当列表为空时如何停止谓词? (包括谓词)
【发布时间】:2019-03-14 19:29:28
【问题描述】:
course(cmput325).
course(cmput175).
course(cmput201).
course(cmput204).
prerequisite(cmput204, cmput325).
prerequisite(cmput175, cmput201).
prerequisite(cmput175, cmput204).

我需要写一个新的谓词,即

can_take(+L,?C).

定义:

L 是学生已经学习过的课程的给定列表。如果 C 也给出了,那么谓词应该检查学生是否拥有 C 的所有必修课程。如果 C 是一个变量,那么通过回溯,谓词应该一次产生学生现在可以学习的一门课程。课程可以按任何顺序排列,但每门课程只能生成一次,并且您不应返回学生已经学习过的任何课程。

例子:

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

L = [cmput201, cmput204].

这是我的谓词:

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

这个谓词没有返回正确的结果,它只是返回了假。我认为这是因为我没有确定 L 为空时的条件,但是,如果我尝试在其中任何一个中添加 L \== [] 。它仍然给了我错误......我应该怎么做才能让这个谓词停止并给我结果?

-------更新-------

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

这是我的代码测试:

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

最后一个不好,因为我不希望它返回两个真实的陈述......所以我想要的是让它在第二行或最后一行返回 true 时停止。对于我的作业,我不允许使用剪切运算符!,还有其他方法吗?

【问题讨论】:

  • 当您添加L \== [] 时,您会在添加更多条件时使情况变得更糟。
  • @false: 这会为查询产生错误的结果findall(C, can_take([], C), L).
  • @gusbro:有没有办法解决这个问题?
  • @gusbro: 哦,等等,can_take([]._)实际上有效,我添加了这个事实而不是替换我的第一个事实。(我检查了 findall,它实际上也有效)
  • @CodeVanessa:这个查询findall(C, can_take([], C), L). 会问你在没有上过任何课程的情况下可以选修哪些课程?

标签: list recursion prolog predicate backtracking


【解决方案1】:

(我假设你可以再学习一门课程,即使你已经学习过了。至少这是我所知道的规则。)

只要你已经修完了所有必修课,你就可以选修一门课程。

Prolog 中没有直接的“全部”。但是你可以用不同的方式来表述这个

您可以参加课程,前提是没有您尚未参加的必修课程。

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

这使用iwhen/2 来防止Takens 未完全实例化的情况。

请注意,您的示例略有不同:

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

免责声明
您的问题本质上是非单调的:通过为要求添加更多事实,您正在减少您可能参加的课程数量。作为初学者,宁愿坚持本质上单调的问题。 Prolog 真正擅长的正是这一方面。

【讨论】:

  • 呃,实际上是 ?- findall(C, can_take([cmput175], C), L) 的正确结果。是 L = [cmput201, cmput204]。
  • @CodeVanessa:这不是你所说的。
  • @CodeVanessa 很容易修复。首先,通过声明L 需要完全接地,您无需防范它未完全接地,然后您只需为其添加成员调用,即可获得:can_take(Takens, Next) :- course(Next), \+ ( prerequisite(Required, Next), \+ member(Required, Takens) ), \+ member( Next, Takens).。通过开始 course(Next),它也保证不会有任何重复。 生成一个可能的候选者,然后测试它的适用性。
  • 在例如C 他们不在乎调用者是否滥用了某些功能;他们称之为“未定义的行为”并继续前进。
  • @Will:在 SO 上不推荐在 C 中暴露未定义行为的代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-09-29
  • 1970-01-01
  • 2011-08-21
  • 2012-04-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多