我需要 "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
你说:只要有X
、Y
和Z
使得右手为真,我们可以得出结论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 引擎总是从第一条规则开始;只有在第一条规则失败后才会尝试以下规则。
我在大学学习 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
你说:只要有X
、Y
和Z
使得右手为真,我们可以得出结论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 引擎总是从第一条规则开始;只有在第一条规则失败后才会尝试以下规则。