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

Recursion GNU Prolog-递归问题(容易吗?),recursion,prolog,Recursion,Prolog,好的,我有这个 edu_less(hs,college). edu_less(college,masters). edu_less(masters,phd). 我需要写一个函数来判断某个东西是否比另一个小。谓词是 edu_le. 因此,如果我输入edu_le(hs,phd)。它应该返回yes。 我想到了这个 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_less(hs,college).
edu_less(college,masters).
edu_less(masters,phd).
我需要写一个函数来判断某个东西是否比另一个小。谓词是

edu_le.
因此,如果我输入
edu_le(hs,phd)。
它应该返回yes。 我想到了这个

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来说真的很陌生,仍在努力掌握这个窍门。

编写这样的谓词最实用的方法是使用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).

最后一个条款不需要删减,因为无论如何没有进一步的条款。在任何测试之后放置切口,以确定该子句是否应成功


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

/0也使得这个程序不完整,例如考虑两个版本的最通用查询:

?- edu_le(X, Y).
如果您只需要一个特定目标的单一证明,通常最好使用once/1:

?- once(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).
第二条是多余的,会导致重复生成答案。使用

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.

按照mat的建议,使用
一次/1
。在一个好的Prolog实现中,这个谓词的工作方式就好像您的程序在战略位置进行了裁剪,在不干扰其逻辑语义的情况下加快了程序的速度。

我建议您不要遵循JuhoÖstman提出的路径并保持纯洁性-否则,为什么您首先要使用Prolog?如果你过于宽容地坚持逻辑范式,你会得到一些令人不快的结果。在本例中,Juho的谓词肯定不同于您的谓词,我将向您说明原因

首先,就像拉斯曼建议的那样,只需删除无用的
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
的答案更少)。你可能只是对它感到高兴,但它仍然有一些多余的答案,你可能不喜欢;e、 g,在SWI下:

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

确实,您消除了无用的final
false

?- edu_le2(hs, hs)
true.

?-
但你失去的不止这些:正如mat所言,当一个变量没有实例化时,你失去了生成所有解决方案的可能性:

?- 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_-le
edu-le1
具有type
edu-le(?A,B)
,而
edu-le2
具有type
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).
请注意,只有当
A
B
恰好是同一术语时,第一条规则中的附加剪切才会激活。既然切割是正确的绿色切割,谓词在大多数情况下也与原始切割一样有效:

?- 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序言参考手册,第4.1节。

ahhhhhhh(就像神圣的ahhh一样)。非常感谢。谢天谢地,我不是纯粹主义者,对吧?
?- 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.

?-