当我调用`even(3)`,`even`是一个生成函数时会发生什么?

时间:2013-09-11 20:27:09

标签: prolog failure-slice

我在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已经给出,这里有什么好处?

4 个答案:

答案 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的参数循环。所以它会为任何查询循环

有关详情,请参阅

答案 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/1even/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实现之间的双向。