Prolog,排列代码理解

时间:2015-11-13 12:02:02

标签: list prolog

我试图了解this计划是如何运作的。

来自Daniel Lyons的代码'解决方案(来自上面的链接)

takeout(X,[X|R],R).  
takeout(X,[F |R],[F|S]) :- takeout(X,R,S).

perm([X|Y],Z) :- perm(Y,W), takeout(X,Z,W).  
perm([],[]).

我尝试了解它如何使用此列表[1,2,3] 所以,我有perm([1,2,3],X).

一开始很容易理解,Y = [2,3]然后Y = [3]然后Y = []

之后调用perm([],[]).,它会给我们W = []

现在,第一次调用takeout - takeout(3, Z, []). 它返回Z = [3]

现在,我们回去了,perm([],[]).给了我们W = [3],(因为此时Y是[3])

与上述相同,takeout(2, Z, [3])Z = [2, 3]

再次perm([], []).W = [2, 3]

takeout(1, Z, [2, 3]),它首先给出了答案Z = [1, 2, 3]

在这里,我不知道为什么程序不会结束,递归完成,为什么takeoutperm再次有效?

之后takeout被称为takeout(1, [2,3])

哪个适用于takeout(X,[F |R],[F|S]),而不适用于takeout(X,[X|R],R).,这是我的第二个问题,为什么?

1 个答案:

答案 0 :(得分:1)

在Prolog中,谓词的行为与过程语言中的函数完全不同。调用函数来执行任务,执行,然后返回返回某些值或执行一些副作用,或两者兼而有之。

谓词定义了在其参数之间建立逻辑连接的关系和/或事实集。当对Prolog中的谓词进行查询时,Prolog将尝试查找参数变量的每个实例化,这将使该谓词成功(为真)。

在一个非常简单的案例中,我可能会有以下事实:

likes(tom, mary).    % Tom likes Mary
likes(fred, mary).   % Fred likes Mary

这里我有一个谓词或事实likes,它定义了两个人之间的关系。我们称之为事实,因为它们都指定了与完全实例化的参数的精确,具体的关系。我可以进行查询以确定谁喜欢玛丽?,如下所示:

| ?- likes(Person, mary).

Person = tom ? ;

Person = fred

yes

查询首先返回Person = tom,但表示一旦找到Person = tom满足查询,就会有更多选项可供检查。输入;告诉Prolog继续下一个解决方案(如果有的话),它会找到它:Person = fred

现在让我们考虑takeout/3。这是一个谓词,它定义了一组变量之间的 relation

  1. takeout(X,[X|R],R).
  2. takeout(X,[F|R],[F|S]) :- takeout(X,R,S).
  3. takeout/3谓词为关系提供了两个谓词子句规则。尝试阅读它们很有帮助:

    1. R是您从X中取出[X|R]后获得的。
    2. [F|S]是您从X 中取出[F|R]时获得的,如果 S是您获取的X R } {} {}} {。}}。{/}

      Prolog以分离的方式看待多个条款。也就是说,如果任何一个规则可以成立,则对谓词的查询或调用将成功。当对takeout/3进行查询时,Prolog将在查询中查找给定变量的实例化,这将使其成立,并且它将尝试查找每个这样做的实例化。换句话说,如果满足条件的方法不止一种,它将回溯并尝试找到那些这样做的变量实例。

      考虑查询:

      ?- takeout(X, [1,2,3], R).
      

      Prolog能够通过实例化takeout(X, [X|R], R)takeout(1, [1,2,3], [2,3])将其与第一个谓词子句X = 1匹配为R = [2,3]。因此,此查询将成功执行以下结果:

      R = [2,3]
      X = 1 ?
      

      但是我们看到Prolog表明还有更多选择可供探索。那是因为还有另一个条款:takeout(X,[F|R],[F|S])与查询takeout(X, [1,2,3], R)匹配。因此Prolog 回溯并尝试第二个子句,匹配:

      takeout(X, [1|[2,3]], [1|S]) :-    % F = 1, R = [2,3]
          takeout(X, [2,3], S).
      

      Prolog将跟随递归调用takeout(X, [2,3], S)再次从第一个子句开始并尝试将takeout(X, [2,3], S)takeout(X, [X|R], R)匹配,后者与{{{ 1}}和X = 2S = [3]。递归展开或返回(就像在任何语言中一样),然后前一个调用头takeout(2, [2|[3]], [3]).最终实例化为:{{1我们得到了:

      takeout(X, [1|[2,3]], [1|S])

      等等。类似行为适用于takeout(1, [1|[2,3]], [1|[3]])。在查询R = [2,3] X = 1 ? ; R = [1,3] % that is, [1|[3]] X = 2 ? 的上下文中,对perm的调用会回溯以产生其他结果,因此perm会产生额外的结果(因为它调用takeout回溯,就像它们一样当您手动查询perm时执行此操作。

      <小时/> 正如@false所指出的,谓词takeout在Prolog中被实现为takeout的标准谓词。