逻辑命令 ********* **(本章需要更多内容和实例)** 本章重点介绍dao系统的逻辑命令。逻辑命令与逻辑程序设计相关。纯函数式程序语言中,根据调用层次和代码布局来看,目标求值总是按照从上到下,从左到右的次序进行,而且进行过程中,所有目标总是成功,不存在会失败的求解目标,也不存在回溯的机制。而逻辑程序则不同,目标可能有多次求解的机会,并且可以因为合一和目标求解失败而引起系统自动回溯。但是纯逻辑系统求解的目标不具有返回值,只能通过变量的合一传递数据。dao系统综合了两种系统的特征,既可以象函数一样,通过传递参数和返回值传递数据,组织函数调用层次管理控制流,也可以通过合一传递数据,通过规则匹配,依据求解成功与失败控制回溯来管理控制流。有了这些手段,我们就可以更轻松的构造更加复杂的程序。正因如此,dao系统的命令也就具有了更多的特性。根据需要,不同命令可能具有多种甚至无数求解方案,或者其求解有可能成功也可能失败,或者永远失败。这些命令,如果成功,会返回一定的值,如果失败,则根本不会返回任何结果,而是发生回溯。也有的命令表现得很象普通的函数,只求解一次并成功返回结果。这就需要我们在实际运用中更多地了解这些命令的不同特性。 依据逻辑命令在求解机制上的特点,我们将它们分成两类: * 函数(function): 这一类命令的执行类似于普通语言中的函数,它们只有一种求解方案,并且总是会成功地返回一个结果。 * 逻辑谓词(predicate):这一类命令的执行可能有多种方案,也可能会失败。如果成功,它们能够返回结果。但是实际使用中,我们也可能只关心它们的成功失败,而并不关心它们的返回结果。 在后面的叙述中,对于需要特别区分的命令,我们将特别注明其分类。至于那些用途明显的命令,就没有一一标注。 逻辑控制 ------- * 谓词and_p: 逻辑与谓词 格式: and_p( *calls ) 如果calls中所有子目标都成功,则and_p(*calls)成功。 >>> and_p(succeed, succeed) True >>> and_p(succeed, fail) Traceback (most recent call last): File "e:\dao\dao\dinpy\term.py", line 1, in from dao.builtins.term import * File "e:\dao\dao\term.py", line 325, in __repr__ result = interactive_solver().eval(code) File "e:\dao\dao\solve.py", line 192, in eval raise NoSolutionFound(exp) dao.solve.NoSolutionFound: exit! >>> and_p(True,True) True >> > and_p(True,False) False >>> and_p(False,True) True >>> and_(True, False) False >>> and_(True, True) True >>> and_(False, True) False 上述实例体现了逻辑谓词与函数的明显不同。True本身是个值,但是也可以看成常量函数,即不需参数,并且重视返回True。在此意义上,False和True是同一类型的物体,只是返回值不同而已。系统对它们求值时,只有一种求解方案,没有被选方案,而且总是成功地返回结果。因此,使用and_p对它们求值的时候,and_p也总是成功的。由于and_p在实现上总是返回后一目标的结果,这才使得上述三次调用具有如此结果。 我们要将逻辑与谓词和python的and运算区别开,也要将它dao系统本身的逻辑与运算and__区别开。一般来讲,我们使用and_p谓词的时候,是希望利用子目标call1和call2的成功与失败的组合控制程序的执行来得到不同结果,因此,我们应该注意子目标应该都是适当的逻辑谓词,而不是常量或者普通函数。下面介绍的or_p谓词也具有同样的特点。 本节的逻辑控制谓词都是基于这种目的设计的,因此在使用上我们要研究它们共同的特点,避免错误的用法。 * 谓词or_p: 或谓词 格式: or_p( *calls ) 如果calls有一个子目标成功,则or_p(*calls)成功。 >>> or_p(succeed, fail) True >>> or_p(fail, fail) Traceback (most recent call last): File "e:\dao\dao\dinpy\term.py", line 1, in from dao.builtins.term import * File "e:\dao\dao\term.py", line 325, in __repr__ result = interactive_solver().eval(code) File "e:\dao\dao\solve.py", line 192, in eval raise NoSolutionFound(exp) dao.solve.NoSolutionFound: exit! >>> or_p(True,True) True >> > or_p(True,False) True >>> or_p(False,True) False 从or_p(True,True),or_p(True,False),or_p(False,True)的求值,可以知道,True, False的求值总是成功成功返回值本身,而or_p总是返回第一个成功的子目标的值,这就是上述例子的表现。 * 谓词if_p: 条件谓词 格式: if_p(antecedent, consequent) 如果antecedent成功并且consequent成功,则if_p(antecedent, consequent)成功,否则失败。 if_p的上述语义是依据 Prolog的ISO的标准,也是各个prolog实现的事实标准。请参考prolog的有关文档。 >>> if_p(succeed, succeed) True >>> if_p(succeed, fail) Traceback (most recent call last): File "e:\dao\dao\dinpy\term.py", line 1, in from dao.builtins.term import * File "e:\dao\dao\term.py", line 325, in __repr__ result = interactive_solver().eval(code) File "e:\dao\dao\solve.py", line 192, in eval raise NoSolutionFound(exp) dao.solve.NoSolutionFound: exit! >>> if_p(fail, succeed) Traceback (most recent call last): File "e:\dao\dao\dinpy\term.py", line 1, in from dao.builtins.term import * File "e:\dao\dao\term.py", line 325, in __repr__ result = interactive_solver().eval(code) File "e:\dao\dao\solve.py", line 192, in eval raise NoSolutionFound(exp) dao.solve.NoSolutionFound: exit! >>> if_p(fail, fail) Traceback (most recent call last): File "e:\dao\dao\dinpy\term.py", line 1, in from dao.builtins.term import * File "e:\dao\dao\term.py", line 325, in __repr__ result = interactive_solver().eval(code) File "e:\dao\dao\solve.py", line 192, in eval raise NoSolutionFound(exp) dao.solve.NoSolutionFound: exit! 与and_p和or_p类似,使用if_p也要注意它作为逻辑谓词的特点。if_p是否继续对consequent求值,是依据antecedent求值的成功与否,而不是依据求解antecedent得到的返回值。这是它在使用上要特别予以注意的。试比较如下两次求解过程:: >>> if_p(False, print(1)) 1 >>> if_p(fail, prin(1)) Traceback (most recent call last): File "e:\dao\dao\dinpy\term.py", line 1, in from dao.builtins.term import * File "e:\dao\dao\term.py", line 325, in __repr__ result = interactive_solver().eval(code) File "e:\dao\dao\solve.py", line 192, in eval raise NoSolutionFound(exp) dao.solve.NoSolutionFound: exit! * 谓词not_p: 否定谓词 格式: not_p(goal) 类似于prolog,dao系统采用了失败作为否定的实现机制。如果goal失败,则not_p(goal)成功,否则not_p(goal)失败。 >>> not_p(True) Traceback (most recent call last): File "e:\dao\dao\dinpy\term.py", line 1, in from dao.builtins.term import * File "e:\dao\dao\term.py", line 325, in __repr__ result = interactive_solver().eval(code) File "e:\dao\dao\solve.py", line 192, in eval raise NoSolutionFound(exp) dao.solve.NoSolutionFound: exit! >>> not_p(False) Traceback (most recent call last): File "e:\dao\dao\dinpy\term.py", line 1, in from dao.builtins.term import * File "e:\dao\dao\term.py", line 325, in __repr__ result = interactive_solver().eval(code) File "e:\dao\dao\solve.py", line 192, in eval raise NoSolutionFound(exp) dao.solve.NoSolutionFound: exit! >>> not_p(succeed) Traceback (most recent call last): File "e:\dao\dao\dinpy\term.py", line 1, in from dao.builtins.term import * File "e:\dao\dao\term.py", line 325, in __repr__ result = interactive_solver().eval(code) File "e:\dao\dao\solve.py", line 192, in eval raise NoSolutionFound(exp) dao.solve.NoSolutionFound: exit! >>> not_p(fail) True >>> not_(False) True >>> not_(True) False >>> not_(succeed) False >>> not_(fail) Traceback (most recent call last): File "e:\dao\dao\dinpy\term.py", line 1, in from dao.builtins.term import * File "e:\dao\dao\term.py", line 325, in __repr__ result = interactive_solver().eval(code) File "e:\dao\dao\solve.py", line 192, in eval raise NoSolutionFound(exp) dao.solve.NoSolutionFound: exit! 注意not_函数和not_p谓词的区别。一般用法要求not_p的参数是个谓词,如果是普通函数,由于普通函数总是会成功返回结果,因此not_p将总是失败。而not_(x)函数的作用是求参数的逻辑反,相当于python的not x。 * succeed: 成功谓词 格式: succeed 成功一次。 * fail: 失败谓词 格式: fail 失败一次。 从前面的实例中已经看到了succeed和fail的用法。 * findall: 求所有解 格式: findall(call, template=None, result=None) >>> let( f << fun()[2][3] ) .do[ findall(is_(x, f()), x, y), prin(y) ] [2, 3] findall求目标call的所有可能的解,如果参数result不是None,则求解过程中将依据模板template收集数据到一个列表中,然后与result进行合一。模板可以是任何可以进行合一的对象,但是系统并不对模板求解,因此要避免使用函数,宏之类的命令作为模板参数。 上述命令中,先求findall需要求的is_(x, f())的全部解集。is_命令的作用是先求它的第二个参数的解,然后设置为第一个参数合一。因为f()定义了两个函数体,因此可以进行两次求解,分别返回2和3,每次合一到x后都被收集到结果列表,最终合一到了结果变量y。 模板参数和结果参数可以是自由变量,也可以是非自由变量或者是数据。如果结果参数不是自由变量,有可能引起findall执行的失败。可以利用这一特性判断某一命令的全部解集是否符合指定的数据值。 * call: 求解谓词 格式: call(goal) 先取得goal的值(注意,使用getvalue命令取值,不是求解),然后对取得的值求解。 >>> is_(x, quote(prin(1)))&call(x) 1 * once: 一次求解谓词 格式: once(goal) >>> findall(once(prin('1, ')|prin('2, '))) 1, True * 重复求解谓词(repeat) 格式: repeat >>> let( i<<0 ). do[ repeat, prin(i), ++i, iff(i<3).do[fail] ] 0 1 2 repeat/fail构造的循环和loop类型的循环有所不同。repeat的循环执行必须依靠fail命令引发失败,回溯到repeat之后重新进行另一轮求解,回溯过程中遇到的合一,解析状态改变都将一一恢复到之前的状态。而在loop语句是正常的前向执行,不存在回溯,也不会有回退合一以及解析状态的动作。 * cut: 截断谓词 格式: cut cut是prolog引进的削减求解空间,提高执行效率的机制。dao系统的实现遵循prolog的标准。当在用户定义的规则体中遇到cut,则冻结已经求得的解,当失败引起回溯时,不再尝试当前用户定义目标(用户定义的函数或者宏)的其它可选求解路径。 >>> letr( a << fun(x) [ b(x)&cut&c(x) ], b << fun(1) [True] (2) [True] (3) [True], c << fun(1) [True] ).do[ a(x), x ] 1 >>> letr( a << fun(x) [ b(x)&cut&c(x) ], b << fun(1) [True] (2) [True] (3) [True], c << fun(1) [True] ).do[ a(x), x ] Traceback (most recent call last): File "e:\dao\dao\dinpy\term.py", line 1, in from dao.builtins.term import * File "e:\dao\dao\term.py", line 325, in __repr__ result = interactive_solver().eval(code) File "e:\dao\dao\solve.py", line 192, in eval raise NoSolutionFound(exp) dao.solve.NoSolutionFound: exit! 看下面两段代码,因为截断谓词的作用,因为冻结了b(x)的求解分支,不再回溯它的另一分支,导致第一段代码的唯一结果为3,后一段代码第一遍求解的结果为4。 >>> letr( a << fun(x) [ b(x)&cut&c(x) ] [ d(x) ], b << fun(1) [True] (4) [True], c << fun(4) [True], d << fun(3) [True], ).do[ a(x), x ] 3 >>> letr( a << fun(x) [ b(x)&c(x) ] [ d(x) ], b << fun(1) [True] (4) [True], c << fun(4) [True], d << fun(3) [True], ).do[ a(x), x ] 4 变量和项 ------------ * unify: 合一谓词 格式: unify(x, y) * is_: 变量与求得的值合一 格式: is_(x, exp) * setvalue: 变量设值 格式: setvalue(var, value) * getvalue: 取值 格式: getvalue(item) * getvalue_default: 默认取值 格式: getvalue_default(item, default=None) * 函数free: 是自由变量? 格式: free(var) * 谓词free_p: 是自由变量? 格式: free_p(var) * 函数bound: 是绑定变量? 格式: bound(var) 如果var被绑定到其它对象,包括被绑定到变量,则返回True,否则返回失败。 * 谓词bound_p: 是绑定变量? 格式: bound(var) 如果var被绑定到其它对象,包括被绑定到变量,则成功并返回True。否则失败。 * 函数ground: 是否实值 格式: ground(item) * 谓词ground_p: 是否实值 格式: ground_p(item) * unbind: 去除变量绑定 格式: unbind(var) 去除变量绑定,使得var成为自由变量 * 函数isvar: 是变量? 格式: isvar(item) * 谓词isvar_p: 是变量? 格式: isvar(item) * 函数nonvar: 不是变量? 格式: nonvar(item) * 谓词nonvar_p: 不是变量? 格式: nonvar_p(item)