2010-10-18 58 views
3

好了,我有这个GNU序言 - 递归问题(?易)

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不到大学,而且大学不是大师,而且大师不到phd,那么hs必须小于phd,并且会说是。

对不起,对于prolog来说真的很陌生,仍然试图解决这个问题。

回答

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

最后一句话,不需要切割,因为没有其他的条款考虑反正。在任何测试之后放置剪切以确定该子句是否应该成功。

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

+0

AHHHHHHHH(像圣洁啊)。谢谢。感谢上帝,我不是纯粹主义者吧? – Matt 2010-10-18 04:35:02

2

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

?- edu_le(X, Y). 

它往往是更好,如果你只想要一个特别的单身证明使用一次/ 1目标:

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

在谓词定义

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

第二克劳斯e是多余的并且导致重复产生的答案。使用

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

我建议你不要遵循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节。