GNU Prolog - 递归问题(容易吗?)

时间:2010-10-18 04:08:44

标签: recursion prolog

好的,我有这个

edu_less(hs,college).
edu_less(college,masters).
edu_less(masters,phd).

我需要编写一个函数来判断某些东西是否比另一个小。谓词是

edu_le.

所以,如果我放edu_le(hs,phd).,它应该返回是。 我想出了这个。

edu_le(A,B) :- A = B.
edu_le(A,B) :- edu_less(A,B).
edu_le(A,B) :- edu_less(A,C), edu_le(C,B).

我真的不希望它通过所有内容并返回多个答案。

如果它发现它实际上小于或等于第二个参数,是否可以只返回yes或no?

所以基本上如果我再次使用示例edu_le(hs,phd),那么因为hs不是大学,而大学不是大师,而且大师不是博士,那么hs必须小于博士,它会说是

对不起,prolog真的很新,仍然试图掌握这一点。

4 个答案:

答案 0 :(得分:4)

在谓词定义中

edu_le(A,B) :- A = B.
edu_le(A,B) :- edu_less(A,B).
edu_le(A,B) :- edu_less(A,C), edu_le(C,B).

第二个条款是多余的,会导致重复生成答案。使用

edu_le(A,B) :- A = B.
edu_le(A,B) :- edu_less(A,C), edu_le(C,B).

这会为您提供一个true答案,然后在回溯时不再提供答案(false)。您可以在第一个子句中使用剪切,但随后生成将不再起作用。

?- edu_le(hs,X).
X = hs ;
X = college ;
X = masters ;
X = phd ;
false.

变得不完整:

?- edu_le(hs,X).
X = hs.

如建议的那样,请改用once/1。在一个很好的Prolog实现中,这个谓词就好像你的程序在战略位置有所削减,加速你的程序而不会打扰它的逻辑语义。

答案 1 :(得分:3)

编写类似谓词的最实用的方法是使用cut(!)。这种削减导致在回溯时不考虑进一步的条款。您可以将谓词编写如下:

edu_le(A,B) :- A = B, !.
edu_le(A,B) :- edu_less(A,B), !.
edu_le(A,B) :- edu_less(A,C), edu_le(C,B).

最后一句不需要削减,因为无论如何都没有其他条款需要考虑。在任何测试之后放置剪切以确定该子句是否应该成功。

逻辑编程纯粹主义者不赞成削减,因为它使谓词的含义取决于子句的顺序,这与数学中的逻辑不同。

答案 2 :(得分:3)

!/ 0也使该程序不完整,例如考虑两个版本的最常规查询:

?- edu_le(X, Y).

如果您只想要一个特定目标的单一证明,通常最好使用一次/ 1:

?- once(edu_le(hs, phd)).

答案 3 :(得分:1)

我建议你不要遵循JuhoÖstman提出的路径并保持纯度 - 否则,为什么你应该首先使用Prolog?如果你对遵守逻辑范式过于宽容,那么你会得到一些不愉快的结果。在这种情况下,Juho的谓词肯定与你的不同,我会告诉你原因。

首先,正如larsmans所暗示的那样,放弃无用的edu_le(A,B) :- edu_less(A,B).规则。您将获得原始谓词的冗余版本:

edu_le1(A, A).
edu_le1(A, B) :- edu_less(A, C), edu_le1(C, B).

它只是表现为edu_le,意思是:给定一个任意查询,它会产生完全相同的答案,但重复项除外(edu_le1有更少)。你可能会对它感到满意,但它仍然有一些你可能不喜欢的多余答案;例如,在SWI下:

?- edu_le1(hs, hs)
true ;
false.

现在你可能会说你不喜欢它,因为它仍然有多余的false,但如果你使用Juho的谓词(没有无用的规则):

edu_le2(A, A) :- !.
edu_le2(A, B) :- edu_less(A, C), edu_le2(C, B).

确实消除了无用的最终false

?- edu_le2(hs, hs)
true.

?-

但是你输的不止于此:当你没有实例化一个变量时,你会失去产生所有解决方案的可能性:

?- edu_le1(hs, B) %same, with more copies, for edu_le
B = hs ;
B = college ;
B = masters ;
B = phd ;
false.

?- edu_le2(hs, B)
B = hs.           %bad!

?-

换句话说,后一个谓词不等同于前者:edu_leedu_le1的类型为edu_le(?A, ?B),而edu_le2的类型为edu_le2(+A, +B) (见[1]的含义)。请确保:edu_le2不太有用,因为它做的事情较少,因此可以在较少的上下文中重复使用。这是因为edu_le2中的切割是红色切割,即切割会改变引入它的谓词的含义。尽管如此,你可能会对它感到满意,因为你了解两者之间的区别。这一切都取决于你想用它做什么。

如果您想要充分利用这两个世界,您需要在edu_le1中引入适当的绿色切割,以便在A和B完全实例化为术语时降低冗余度。在此目的,您必须检查A和B是否在切割前实例化为相同的术语。您无法使用=执行此操作,因为=不会检查,而是统一。合适的运营商是==

edu_le3(A, B) :- (A == B -> ! ; true), A = B.
edu_le3(A, B) :- edu_less(A, C), edu_le3(C, B).

请注意,仅当AB恰好是同一字词时,第一条规则中的附加剪辑才有效。既然剪切是一个适当的绿色剪切,那么谓词在最常见的情况下也可以作为原始剪辑使用:

?- edu_le3(A, A).
true.

?- edu_le3(A, B).  %note that A and B are not the same term
A = B ;
A = hs,
B = college ;
A = hs,
B = masters ;
A = hs,
B = phd ;
A = college,
B = masters ;
A = college,
B = phd ;
A = masters,
B = phd ;
false.

?-

Prolog回溯所有解决方案。

我认为,如果不对false引入太强的依赖性,有一些方法可以消除上一个edu_lt。这是因为我们必须保持有另外edu_lt探索的可能性,如果您稍后决定用更多基础事实来丰富它。所以,在我看来,这是你能做到的最好的。

[1] SWI Prolog参考手册,第4.1节。