4.4逻辑编程

在第一章中, 我们了解了计算机科学是处理过程式的知识,而数学是处理声明式的知识。
的确,编程语言要求程序员表达知识时,以一种特定的形式。这种形式显示出解决特定问题的一步步的方法。
另一方面,高层次的语言提供了,作为语言实现的一部分,大量的方法论的知识。这些知识把用户从具体执行的
大量的细节中解放出来。

大部分的编程语言,包括lisp,被组织成计算数学函数的值。面向表达式的语言(例如lisp,fortran,algol)
评估表达式的双关语。表达式描述了一个函数的值,也可能被解释为计算那个值的方法。
正因为此,绝大部分的编程语言都强烈地倾向于单方向的计算。也就是计算是有定义好的输入和输出。
然而,有完全不同的编程语言解除了这种偏见。我们在3.3.5部分中看到了一个这样的例子,
在那里,计算的对象有算术约束。在一个约束的系统里,计算的方向和次序没有被很好地指定。
在执行一个计算时,系统必须提供更多的如何做的细节知识,这些知识远比普通的算术计算的知识多。
然而,这并不是意味着,用户能从提供过程式的知识的责任中完全解脱出来。这有实现相同的约束的集合的许多的约束网络。
为了一个指定的特定的计算,在数学上等价的网络的集合中,用户必须选择一个合适的网络。

4.3部分中,非确定性程序的评估器 也改变了那个观点。观点是编程是为了计算单一方向的函数,
而组装起算法。在一个非确定性的语言中,表达式能有超过一个值,作为结果,计算处理关系,而不是
一个单值函数。 逻辑编程扩展了这个思想,通过组合一个关系的编程美景。
这种编程有一种强有力的符号模式匹配,这种匹配称为统一。

这个方法,当它在使用时,是写程序时的强有力的方式。力量的部分来源是
是一个事实。这个事实是一个单一的“是什么”的事实能被用来解决一系列不同的问题。
这些问题有不同的“怎么做”的组件。作为一个例子,考虑一下,append操作,
它有两个列表作为参数,组合它们的元素形成一个单一的列表。在一个程序化的语言中,
例如lisp,我们能定义append,用基本的列表组装子,cons,正如我们在2.2.1部分中使用的那样。

(define (append x y)
   (if (null? x)
       y
      (cons (car x) (append (cdr x) y))
   )
)

这个程序被认为是如下的两条规则被翻译成了Lisp语言。规则之一是覆盖了
第一个列表是空的情况,第二个规则是处理一个非空的列表的情况,它使用cons组装这两个部分。

对于任何一个列表y,空列表和y 进行append操作,得到y.
对于任何u,v,y,z,如果v和y进行append操作,形成z,那么
(cons u v)和 y 进行 append 操作,形成(cons u z)。

使用这个程序append,我们能回答如下的问题,例如

   找到 (a b) 和( c d) 的append 的结果。

但是对于append程序不能解决的,如下的排序的问题,
这两条相同的规则也是充分的。

   找到一个列表y,y和(a b) 进行append 操作,生成(a b c d)

   找到所有的 x 和y 列表,x和y进行append 操作,生成(a b c d)

在一个逻辑编程语言中,程序员写一个"append"程序,通过声明关于append的如上的两条规则,
"怎么做"的知识由解释器自动地提供,来允许这单一的一对规则被用来回答
关于append的这三种类型的问题。

当代的逻辑编程语言(包括我们实现的这个)有大量的缺陷,例如,
它们的通用化的“怎么做”的方法能够导致它们进入虚假的,无穷的循环之中。
或者是其它的非预期的行为。在计算机科学中,逻辑编程是一个研究的热点区域。

在这一章的前面的部分, 我们探索了实现解释器的技术,描述了解释器的元素。
这些元素对于类Lisp语言的解释器来说,是必须的。其实对于任何一个传统的语言的
解释器来说,都是必须的。 现在,我们应用这些思想,来讨论一个逻辑编程语言的解释器。
我们叫这个语言是查询语言。因为对于从数据库中检索信息,它是很有用的,
通过公式化语言中表达的查询或者是问题。 即使查询语言非常不同于lisp,我们仍然能发现
使用已有的框架,描述语言时的方便性。这个框架包括,原生元素的集合,结合组合的方法,抽象的方法。
组合的方法让我们能把简单的元素组合成复杂的元素。抽象的方法让我们能把复杂的元素作为一个单一的概念。
一个逻辑语言的解释器被认为比一个类lisp的解释器更加复杂 。
然而,我们将看到,我们的查询语言的解释器,包括很多的与在4.1部分中的解释器相同的元素。
特别是, 这有一个"评估"部分,它根据类型分类表达式,还有一个“应用”部分,实现语言的抽象机制。
(这个抽象机制是lisp的程序,逻辑语言的规则)。还有,一个框架性的数据结构的实现中,起核心作用的角色,
这个角色确定了符号和它们关联的值之间的通信。我们的查询语言的实现中,有一个附加的有趣的方面,
是我们大量地使用了在第三章中,被介绍过的流。

 

10-07 13:13