在 Prolog 递归中正确使用 is/2 谓词
Correct use of is/2 predicate in Prolog recursion
我是具有命令式编程背景的 Prolog 初学者。在解决 this site 的作业时,我遇到了两个练习:第一个是关于 找到列表的第 k 个元素 ,第二个是关于 查找列表的长度。这些都是经典的列表处理问题,解决方案非常简单,也适用于像我这样的初学者。我无法理解的是内置 is/2
谓词的(显然)不同用法。就我而言,这个谓词迫使 Prolog 进行算术计算,并可能将结果分配给左侧项。我被告知要注意的是,虽然可以在右侧使用变量,但在评估时必须使用无变量项实例化这些变量 .
对于第一个练习,建议的解决方案如下:
element_at(X,[X|_],1).
element_at(X,[_|L],K) :- K > 1, K1 is K - 1, element_at(X,L,K1).
并且 is
的使用方式对我来说很有意义:因为 K
是作为参数提供的,所以 K1
可以立即被评估并作为递归中的新参数提供call.
通过编写第二个练习的解决方案,即查找列表长度,我得出以下结论:
my_length([], 0).
my_length([_|L], K) :- K is K1 + 1, my_length(L, K1).
并被 Prolog 通知 "arguments are not sufficiently instantiated"。
原来正确的解决方案是这个:
my_length([], 0).
my_length([_|L], K) :- my_length(L, K1), K is K1 + 1.
在这种情况下,递归调用是在之前通过is
评估K。在之前的练习中,它是在 after 评估 K is K-1
.
我想我误解了一些关键概念。在什么情况下 is/2
必须在递归调用之前使用,另一方面,什么时候必须在递归调用之后使用?为什么会这样?
感谢任何帮助和解释(我正在使用 SWI-Prolog 7.6.4)。
is
仅当其右侧术语中的所有变量都已实例化时才应使用。你自己也这么说。因此,在您的 at
谓词中,它的设计使得 K
必须 在查询中调用谓词时已经实例化为数值表达式 - 即,任何其他查询都会导致错误。由于K
已经实例化了,调用is
就OK了。
在第二个谓词中,K
是您期望的 output(可能还有要验证的输入)。由于在进行调用时 not 保证知道,因此必须允许这样的查询工作。这意味着,不能假设 K
是已知的。而如果是未知的,那时候不能调用is
,否则会报错。
因此我们首先进行递归调用,因为它肯定会实例化 K
,如果它还没有的话。
我是具有命令式编程背景的 Prolog 初学者。在解决 this site 的作业时,我遇到了两个练习:第一个是关于 找到列表的第 k 个元素 ,第二个是关于 查找列表的长度。这些都是经典的列表处理问题,解决方案非常简单,也适用于像我这样的初学者。我无法理解的是内置 is/2
谓词的(显然)不同用法。就我而言,这个谓词迫使 Prolog 进行算术计算,并可能将结果分配给左侧项。我被告知要注意的是,虽然可以在右侧使用变量,但在评估时必须使用无变量项实例化这些变量 .
对于第一个练习,建议的解决方案如下:
element_at(X,[X|_],1).
element_at(X,[_|L],K) :- K > 1, K1 is K - 1, element_at(X,L,K1).
并且 is
的使用方式对我来说很有意义:因为 K
是作为参数提供的,所以 K1
可以立即被评估并作为递归中的新参数提供call.
通过编写第二个练习的解决方案,即查找列表长度,我得出以下结论:
my_length([], 0).
my_length([_|L], K) :- K is K1 + 1, my_length(L, K1).
并被 Prolog 通知 "arguments are not sufficiently instantiated"。
原来正确的解决方案是这个:
my_length([], 0).
my_length([_|L], K) :- my_length(L, K1), K is K1 + 1.
在这种情况下,递归调用是在之前通过is
评估K。在之前的练习中,它是在 after 评估 K is K-1
.
我想我误解了一些关键概念。在什么情况下 is/2
必须在递归调用之前使用,另一方面,什么时候必须在递归调用之后使用?为什么会这样?
感谢任何帮助和解释(我正在使用 SWI-Prolog 7.6.4)。
is
仅当其右侧术语中的所有变量都已实例化时才应使用。你自己也这么说。因此,在您的 at
谓词中,它的设计使得 K
必须 在查询中调用谓词时已经实例化为数值表达式 - 即,任何其他查询都会导致错误。由于K
已经实例化了,调用is
就OK了。
在第二个谓词中,K
是您期望的 output(可能还有要验证的输入)。由于在进行调用时 not 保证知道,因此必须允许这样的查询工作。这意味着,不能假设 K
是已知的。而如果是未知的,那时候不能调用is
,否则会报错。
因此我们首先进行递归调用,因为它肯定会实例化 K
,如果它还没有的话。