我需要 "base step" 才能在 Prolog 中进行递归吗?

Do I need "base step" to make a recursion on Prolog?

我在大学学习 Prolog,但遇到了一个问题。 请注意,我是 Prolog 的新手,我什至不知道 Prolog 元素的正确拼写。

我需要在我的 .pl 文件中定义递归规则,但我不知道我的规则是否需要 "base step"。检查我的规则:

recur_disciplinas(X, Y) :- requisito(X, Y).
recur_disciplinas(X, Y) :- requisito(X, Z), recur_disciplinas(Z, Y).

这是可行的,但我不能像下面那样做吗?

recur_disciplinas(X, Y) :- requisito(X, Z), recur_disciplinas(Z, Y).

当我声明相同的 "rule name" (recur_disciplinas(X,Y) :-) 两次时会发生什么?发生有点像覆盖?

我目前正在使用 swi-prolog。十分感谢大家!

如果您多次使用一个规则名称,它会在您的控制流中创建一个 or 分支。 Prolog 将尝试统一第一个子句。如果失败,它将尝试第二个子句和第三个子句,依此类推

在上面的代码中,recur_disciplinas 规则将首先尝试找到匹配的 requisito。如果失败,它将尝试以传递和递归方式找到一个要求中的一个要求。

如果不放base子句,Prolog会一直尝试递归子句,可能会进入死循环。

编写基本条件并不是 Prolog 独有的。每一种允许递归的语言都是一样的。如果没有停止条件,您的函数将进入无限循环。

考虑这个等效的程序伪代码:

def find_disciplinas(X, Y):
  if find_requisito(X,Y): # halting condition
    return (X, Y)
  else: # recursive call
    for all Z such that find_requisito(X, Z):
      return find_disciplinas(X, Z)

如果您的 "requisito" 记录包含一个循环,并且您删除了停止条件,则上述过程将无限循环。

理解 Prolog 规则的最佳方法是查看 :- 运算符,它是 1970 年代的箭头渲染(是的,Pascal 中的赋值运算符 := 表示箭头, 也)。所以你看看右边是什么,然后说:如果一切都是真的,我可以得出左边是什么的结论。所以您正在按照您的规则从右到左阅读:

 recur_disciplinas(X, Y) :- requisito(X, Z), recur_disciplinas(Z, Y).
 %                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ read

你说:只要有XYZ使得右手为真,我们可以得出结论recur_disciplians(X, Y)成立。现在,让我们通过删除 requisito(X, Z) 来概括这一点。现在剩下的是:

 recur_disciplinas(X, Y) :- /******/ recur_disciplinas(Z, Y).

因此您可以从 recur_disciplinas(Z, Y) 得出 recur_disciplinas(X, Y) 成立的结论。但是你没有什么可以从那个结论开始的!如此有效,这意味着根本无法解决此关系。

就像在说,只要我能飞,我就会像鸟一样飞翔。

也许那是真的,但只要你不飞,一切都是徒劳。

参见this answer如何允许更简洁地表达你们的关系。一个目标closure(requisito, X, Y)就够了!它甚至可以处理潜在的循环。


顺便说一句,我怀疑 recur 是某个动词,甚至是祈使句。正确的?尽量避免关系命令。祈使句有利于改变事物。就像 "switch on the light" 一样,它将世界从一盏灯关掉的世界变成了一个开着灯的世界。命令式非常适合告诉无意识的实体该做什么。如果你更想对事情进行推理,那么命令就是不当行为。而是关注什么应该发生,什么不发生。

这里我们说recur_disciplinas/2是一个有两个参数的谓词,你问的是两个从句(规则) 对于谓词是必需的。

正如其他答案所说,递归需要 "base case" 以便递归终止,这通常是可取的!最常见的安排就像你的第一个例子:第一条规则是终止条件(基本情况),第二条规则是递归步骤(归纳情况)。阅读您的代码的人可能会发现这种安排很熟悉且易于理解。

然而,基本情况和递归步骤可以合并为一个规则,这有时很有用。例如,我们可以使用 OR 语法:

recur_disciplinas(X, Y) :-
    requisito(X, Y) ; ( requisito(X, Z), recur_disciplinas(Z, Y) ).

这里 ; 表示 OR,这个单一规则产生的解决方案搜索与原来的双规则版本基本相同。

也可能存在多个基本案例,每个案例都有自己的规则或写入更复杂的 "combination" 规则。与任何编程规则一样,清晰和正确性应该比代码的简洁性更重要。

在一些不寻常的情况下,将递归步骤定位为第一个规则,并将基本案例(或案例)移至后续规则可能是有利的。这需要格外小心以确保始终达到终止条件,因为您不太可能希望代码可以无限循环。当调用谓词时,Prolog 引擎总是从第一条规则开始;只有在第一条规则失败后才会尝试以下规则。