我在prolog中有以下奇数和偶数的生成器
even(0).
even(X) :- odd(Y), X is Y+1, X>0.
odd(1).
odd(X) :- even(Y), X is Y+1, X>1.
我想了解为什么我不能将这些函数用作测试人员,即?even(3).
这导致无限循环。
这不是我拨打?even(3).
时发生的事情吗?
X
被实例化为3
。
尝试找到任何奇怪的Y
(从0
开始)。查找Y=1
。
现在是我不明白的部分。我不知道在必须处理条款X is Y+1
时会发生什么。考虑到X
已经给出,这里有什么好处?
答案 0 :(得分:4)
您正在尝试了解程序的精确终止属性,当您来自程序语言时,这有点令人惊讶。在Prolog中,有几个交错的控制流程,这使得实际执行通常难以遵循。
要理解它,您可以逐步跟踪程序以了解实际发生的情况,但该方法很快就会变得复杂。而且你的程序尽可能简单。相反,我将使用failure-slices向您展示另一种方法。
您很幸运地使用查询even(3)
,它会立即向您显示存在问题。您可以使用其他查询,例如even(2).
,它不会立即显示问题。实际上,Prolog很适合这个查询。一切似乎都很好,除非你要求进一步的答案。
那么我们怎样才能确保我们尽快面对问题呢?一种方法是改为构建查询even(2), false
。在这种情况下,我们希望查询失败,因为false
永远不会成功。但是,查询可能会产生无限循环(或错误),而不是失败。通过最后添加false
,我们会说:跳过所有答案,然后只显示查询是否终止。
现在很好的(纯粹的,单调的)Prolog是我们可以对你的程序做同样的事情。因此,我们可能会在您的计划中添加目标false
。如果生成的程序(称为failure-slice)现在循环,那么原始程序也将实际循环。
这是仍然循环的最小故障切片:
even(0) :- false. even(X) :- odd(Y), false,X is Y+1, X>0.odd(1) :- false. odd(X) :- even(Y), false,X is Y+1, X>1.
正是这个微小的剩余部分负责所有的循环。现在,你不仅可以证明为什么even(2)
循环,而且你可以看到更一般的东西:这个失败切片将独立于even/1
的参数循环。所以它会为任何查询循环 !
有关详情,请参阅failure-slice。
答案 1 :(得分:3)
X is Y + 1
目标变为3 is 1 + 1
并且失败,导致前一次调用odd(Y)
的回溯。这是转向使用odd/1
谓词的第二个子句来尝试找到替代解决方案。第二个子句主体调用even(Y)
将Y
实例化为0
,然后尝试X is 0+1, X>1
,但失败导致回溯到之前对even/1
谓词的调用。从这里开始就像是odd/1
和even/1
谓词之间永无止境的乒乓游戏。您可以使用Prolog编译器trace
功能逐步了解正在发生的事情。
另请注意,在Prolog中,变量是子句的本地变量。也许那令你困惑的是什么?
答案 2 :(得分:1)
当你致电even(3)
时(因此,作为测试),3与0不匹配,因此它属于复合条款。
该子句的第一件事是在未绑定的变量Y上调用odd
,以及作为生成器。它最终成功的唯一方法是odd(Y)
可以返回奇数Y,3 is Y+1
。生成器将返回1,3,5等,这样就不会发生。但是Prolog无法知道这一点,所以它能做的就是尝试所有这些。碰巧有无穷大[*],因此无限循环。
[*]取决于您的Prolog实现的整数设置,但无论它是否实际完成都需要很长时间。
答案 3 :(得分:0)
要使Prolog代码成双向,您必须为其添加子句 不同的模式,并使用元变量测试仪var / 1来决定 两种变体之间:
生成器:
even(0).
even(X) :- odd(Y), X is Y+1, X>0.
odd(1).
odd(X) :- even(Y), X is Y+1, X>1.
测试人员:
even(0).
even(X) :- X>0, Y is X-1, odd(Y).
odd(1).
odd(X) :- X>1, Y is X-1, even(Y).
双向代码:
even(0).
even(X) :- var(X), !, odd(Y), X is Y+1, X>0.
even(X) :- X>0, Y is X-1, odd(Y).
odd(1).
odd(X) :- var(X), !, even(Y), X is Y+1, X>1.
odd(X) :- X>1, Y is X-1, even(Y).
Prolog系统,库和用户代码通过这种技术实现了许多谓词。一个原因可能是使用这种双向谓词,可以简化定义其他双向谓词。
但事情并非那么简单,因为当谓词合并时,目标可能仍需要一些重新排序。所以也许我们只想保存命名空间。 BTW here是/ 3实现之间的双向。