使用 Prolog 在 CLP(R) 中编写递归函数的正确方法

Bra*_*roy 4 recursion prolog clp clpr

我对 CLP 在 Prolog 中的工作方式感到非常困惑。我不仅发现很难看到好处(我确实在特定情况下看到了它,但发现很难概括这些好处),而且更重要的是,我几乎无法弥补如何正确编写递归谓词。以下哪一项是 CLP(R) 方式的正确形式?

factorial(0, 1).
factorial(N, F):- {
  N > 0,
  PrevN = N - 1,
  factorial(PrevN, NewF),
  F = N * NewF}.
Run Code Online (Sandbox Code Playgroud)

或者

factorial(0, 1).
factorial(N, F):- {
  N > 0,
  PrevN = N - 1,
  F = N * NewF},
  factorial(PrevN, NewF).
Run Code Online (Sandbox Code Playgroud)

换句话说,我不确定何时应该在约束之外编写代码。对我来说,第一种情况似乎更合乎逻辑,因为PrevN并且NewF属于约束条件。但如果这是真的,我很想知道在什么情况下在递归函数中使用约束之外的谓词是有用的。

mat*_*mat 5

您的帖子中有几个重叠的问题和问题,可能太多了,无法在单个帖子中连贯地解决您完全满意的问题。

因此,我想先说明一些一般原则,然后在此基础上对您发布的代码进行一些具体评论。

首先,我想谈谈我认为对您而言最重要的问题:

LP ⊆ 中电

这仅仅意味着 CLP 可以被视为逻辑编程 (LP)的超集。是否应将其视为适当的超集,或者实际上是否将它们视为表示相同的概念更有意义,这有些值得商榷。在我个人看来,没有约束的逻辑编程比约束更难理解,也更不可。鉴于即使是第一个 Prolog 系统也有一个约束dif/2,而且基本的内置谓词也 (=)/2完全符合“约束”的概念,边界,如果它们存在的话,对我来说似乎至少有点人为,表明:

LP≈CLP

尽管如此,使用 CLP(任何类型的)时的关键概念是约束可用作 谓词,并像所有其他谓词一样在 Prolog 程序中使用。

因此,无论您是否有目标,factorial(N, F)或者{ N > 0 }至少在原则上是相同的概念:两者都意味着某事 成立

注意语法:CLP(ℛ) 约束的形式{ C }是 {}(C)前缀符号。

请注意,我们的目标factorial(N, F)不是一个CLP(ℛ)约束!以下也不是:

?- {阶乘(N,F)}。
错误:未处理的异常:type_error({factorial(_3958,_3960)},...)

因此,{ factorial(N, F) }不是一个CLP(ℛ)约束要么!

因此,您的第一个示例已经不能仅凭这个原因工作。(此外,您在子句 head: 中有语法错误factorial (,因此它也根本无法编译。)

当您学习使用约束求解器时,请查看它提供的谓词。例如,CLP(ℛ) 提供了{}/1和其他一些谓词,并有一个专用的语法来说明关于浮点数(在这种情况下)的关系。

其他约束求解器提供了自己的谓词来描述各自领域的实体。例如,CLP(FD) 提供(#=)/2了一些其他谓词来推理整数dif/2让您推理任何Prolog 术语。等等。

从程序员的角度来看,这使用Prolog 系统的任何其他谓词完全相同,无论它是内置的还是源自库。原则上,都是一样的:

像这样的目标list_length(Ls, L)可以读作:“列表的长度 Ls是 L。”

像一个目标{ X = A + B }可以读作:数X等于总和A和 B。例如,如果您使用 CLP(Q),很明显我们在这种情况下谈论的是有理数

在您的第二个示例中,子句的主体是形式的连词(A, B),其中A是 CLP(ℛ) 约束,B是形式 的目标factorial(PrevN, NewF)

重点是:CLP(ℛ) 约束也是一个目标!一探究竟:

?- write_canonical({a,b,c})。
{','(a,','(b,c))}
真的。

因此,您只是使用{}/1from library(clpr),这是它导出的谓词之一。

You are right that PrevN and NewF belong to the constraints. However, factorial(PrevN, NewF) is not part of the mini-language that CLP(ℛ) implements for reasoning over floating point numbers. Therefore, you cannot pull this goal into the CLP(ℛ)-specific part.

From a programmer's perspective, a major attraction of CLP is that it blends in completely seamlessly into "normal" logic programming, to the point that it can in fact hardly be distinguished at all from it: The constraints are simply predicates, and written down like all other goals.

您是否将库谓词标记为“约束”几乎没有任何区别:所有谓词都可以被视为约束,因为它们只能约束答案,永远不会放松它们。

请注意,您发布的两个示例都是递归的!这完全没问题。事实上,递归谓词可能是您将来使用约束的大多数情况。

但是,对于factorial的具体情况,您的 Prolog 系统的CLP(FD)约束可能更适合,因为它们完全致力于推理 integers

  • 例如,阅读第二个示例的一个好方法是说:“**If** it is the case that `N > 0`, **and** it is the case that ... etc., **那么**就是`factorial(N, F)`成立的情况”。您如何对约束进行排序可能会产生程序上的影响,但不要让这分散您对子句含义的注意力,以及您可以在不影响声明性含义的情况下自由重新排序目标的事实。 (2认同)