zoukankan      html  css  js  c++  java
  • Learn Prolog Now 翻译

    内容提要

     通过递归对列表进行遍历,从而完成各种操作。

     

     member/2这个谓词逻辑通过递归遍历了列表,对列表头部有一些操作,然后递归地对列表尾部做另外一些相同的操作。通过递归遍历列表在Prolog是十分普遍的做法,

    事实上,我们必须要掌握这项技能。所以我们学习如下的例子

     当我们使用列表的时候,我们经常会将一个列表和另一个列表进行对比,或者拷贝一个列表的内容到另一个列表去,或者翻译一个列表到内容到另一个列表去,或者

    类似到一些操作。这里有一个例子,假设我们有一个谓词a2b/2,有两个参数,第一个参数是a的列表,第二个参数是b的列表,而且两个列表的长度相同;比如,如果

    我们进行查询:

      ?- a2b([a, a, a, a], [b, b, b, b]).

     我们希望Prolog能够回答true。另外一方面,如果我们进行查询:

       ?- a2b([a, a, a, a], [b, b, b]).

     或者进行查询:

       ?- a2b([a, c, a, a],  [b, b, 5, 4]).

     我们希望Prolog能够回答false。

     当我们面对此类任务时,通常最好的解决问题的方式是首先从最简单的情况入手。现在,当使用列表时,思考最简单的情况通常意味着从空列表开始,而且在这个例

    子中确实也是有意义的。毕竟,什么样的列表是关于元素a最简单的列表?是空列表。为什么?因为它没有包含一个a元素。那么关于元素b最简单的列表呢?也是空列

    表。所以我们能够定义出最基础的信息如下:

       a2b([], []).

     这个明确的事实记录了关于a的空列表和关于b的空列表时相等的。虽然这个事实很明确,但是它将会在程序中发挥至关重要的作用,我们稍后就会看到。

     直到现在一切还好,那么如何进行下一步呢?这里有一个思路:对于更长的列表,通过递归去思考。所以,当谓词a2b/2去检查两个非空的列表,一个是关于a的,另

    一个是关于b的,如何确认两个列表有相同的长度呢?很简单:当第一个列表的头部是a,同时第二个列表的头部是b,并且a2b/2能够证明两个列表的尾部有相同的长度,

    我们就能够立刻写出如下的规则:

       a2b([a | Ta], [b | Tb]) :- a2b(Ta, Tb).

     解释一下:a2b/2能够成功的条件是,第一个参数是头部为a的列表,第二个参数是头部为b的列表,同时a2b/2能够在两个列表的尾部操作成功。

     这个定义有了很好的声明性。这是一个简单而又自然的递归谓词,基础子句处理空列表,递归子句处理非空列表。但是它实际是如何工作的?即,它的程序性含义是怎么样的?

    比如,如果我们查询:

      ?- a2b([a, a, a], [b, b, b]).

     Prolog将会回答true,也是我们期望的,但是这一切是如何发生的?

     让我们通过这个例子来学习。在例子中,两个列表都是非空的,所以事实子句不能提供帮助。所以Prolog就尝试使用递归规则,现在,查询能够满足这个规则(因为第一个列表

    头部是a,并且第二个列表的头部是b),所以Prolog有了新的目标,即:

      a2b([a, a], [b, b]).

     再一次地,事实子句不能提供帮助,但是递归规则能够再次被使用,导致接下来的目标是:

      a2b([a], [b]).

     事实子句还是不能提供帮助,但是递归规则可以,所以我们又有了如下的新目标:

      a2b([], []).

     最终我们可以使用事实了:它告诉我们true,我们确实有两个关于a和b的、长度相同的列表(空列表,什么都没有),这意味着如下的目标:

      a2b([a], [b]).

     也是成立的,这会导致目标:

      a2b([a, a], [b, b]).

     也是被满足的,所以原始的目标:

      a2b([a, a, a], [b, b, b]).

     是满足的。

     我们总结这个过程如下:Prolog从两个列表开始,通过检查第一个列表的头部是否为a,第二个列表的头部是否为b,然后去掉两个列表的头部;而后,使用相同的处理方式对两个

    列表的尾部进行操作。为什么这个过程会终止?因为每一次的递归后,列表都会变短,最终因为是空列表,所以会终止。从这个角度来说,程序中的事实会发挥决定性的作用:

    它给出true的回答,并且终止了递归,从而确保原始的查询是成功的。

     了解查询失败也是同样重要的。比如,如果我们查询:

      ?- a2b([a, a, a, a], [b, b, b]).

     Prolog会正确地回答false,为什么?因为经过了去掉头部-循环尾部的处理过程三次后,会剩下如下的目标:

      a2b([a], []).

     但是这个目标无法满足。如果我们查询:

      a2b([a, c, a, a], [b, b, 5, 4]).

     经过去掉头部-循环尾部的处理过程仅仅一次,Prolog会有如下的新目标:

      a2b([c, a, a], [b, 5, 4]).

     同样地,这个目标也无法满足。

     以上是a2b/2简单的使用情况,但是我们还没有完全覆盖完所有的使用场景。Prolog的使用过程中,查询输入变量始终是一个尝试的好方式。这时a2b/2会有一些有趣的事情发生,

    会表现得像一个转换器,将元素a的列表,转换为元素b的列表。比如查询:

      ?- a2b([a, a, a, a], X).

      X = [b, b, b, b]

     即,元素a的列表,已经被转换为元素b的列表。类似地,通过在第一个参数位置使用变量,我们可以将元素b的列表,转换为元素a的列表:

       ?- a2b(X, [b, b, b, b]).

      X = [a, a, a, a]

     你能够根据这个结果知道它是如何发生的吗?总结一下:a2b/2是一个很简单的、通过递归遍历列表的例子。但是不要被它的简单性迷惑:此类程序展示了Prolog的基础功能。

    无论是其声明形式(一个处理空列表的基础子句,一个处理非空列表的递归子句),还是具体执行的程序性(在列表头部做一些操作,然后对其尾部进行同样的递归处理),

    都会在Prolog编程中反复使用。事实上,在你的Prolog生涯中,你会发现你在写各式各样的a2b/2谓词,或者是其更复杂的变体,许多时候加入了很多的装饰,但是本质上是一样的。

  • 相关阅读:
    ‘Host’ is not allowed to connect to this mysql server
    centos7安装mysql
    further configuration avilable 不见了
    Dynamic Web Module 3.0 requires Java 1.6 or newer
    hadoop启动 datanode的live node为0
    ssh远程访问失败 Centos7
    Linux 下的各种环境安装
    Centos7 安装 python2.7
    安装scala
    Centos7 安装 jdk 1.8
  • 原文地址:https://www.cnblogs.com/seaman-h-zhang/p/4639432.html
Copyright © 2011-2022 走看看