AI——Lisp语言实现 合一算法

来源:互联网 发布:mt4软件怎么用 编辑:程序博客网 时间:2024/05/21 17:52

[问题描述]

编程实现表达式合一算法,对任意两个表达式E1E2,找出其最一般合一s

[测试数据]

输入表达式:

E1 = P (x,  f (x),  g (z) )       E2 = P (y,  f ( g (b) ),  y )

输出结果:

s = { g(b)/x ,  g(b)/y ,  b/z }

[实现提示]

     1.用广义表的结构存储表达式

     例如:

     表达式:E1=P(x,f(x),g(z)) 

     表示如下:

  (P x (f x) (g z))

     表达式:E2=P(y,f(g(b)),y)

     表示如下:

     (P y (f (g b)) y)

      2.用广义表存储合一(代换):

      例:

      s={g(b)/x,g(b)/y,b/z}

      表示如下:

      s=(((g b) x) ((g b) y) (b z))

      3.变量表示:

      置特性值(setq) 或 设置变量表(let) 或 与var构成序偶

     这里假设使用变量表:

     varlist=(x y z .......)   

[实现语言]

    lisp 语法采用的是前缀表示,因为它使用广义表的方式将所有的命名,属性,STL,API等都利用广义表进行运算,所以它的计算能力比较强大。

    对于lisp语言的教程可以参考一下连接,建议初学者可以先根据链接里的教程了解一些基本语法和库函数,比如:atom,setq,let,cond,if   car,cdr,append,cons。

    http://www.yiibai.com/lisp/lisp_basic_syntax.html

    lisp的开发环境,网上推荐windows/linux(ubuntu)系列可以装clisp ,mac os我装的是lispWorks personal

无脑下载,安装基本的语言使用以及调试就是可以的。

[实现思路]

    自底向上实现几个板块:

     1.isvar 变量判断 利用系统函数member(X,L)x代表变量,L代表表单

     2.contain包含判断 

      递归算法。

递归结束条件:e 空表 原子,根据具体情况返回真或假

      递归分解方法:将e分解为表头和表尾,分别进行递归调用,根据返回值进一步返回真或者假

     3.代换函数subsitution(e s) 实现 e表示表达式,s表示代换对

     4.合成函数compose,一般调用的是substitution(e s),需要用到循环和递归

     5.合一函数unify(e1 e2)

        利用分解实现:

        将表达式分解为表头和表尾,首先求出表达式的最一般合一 s1,然后将s2作用到表尾上,并求出表尾的最一般合一s2,最后返回的是s2和s2的最一般合一。

[实现代码]:

以下代码中的事例在实现的时候都要转换成广义表的形式,如p(f(x),y) 转换后的形式为: (p (f x) y)

;;在以下的程序中表示注释。

(setq list '(x y z));;命名一个变量的表单,即在以下的程序中出现的x,y,z都可以视为变量(defun isvar(e);;定义一个函数,判断参数e 是否为一个变量    (cond ((member e list) t);;是的话返回为真          (t              nil);;否则返回为假    ))   (defun contain(e x);;判断e中是否包含x    (cond ((null e)    NIL);;如果e为null,那么返回一个空值          ((atom e) (if (equal x e) 'T 'NIL));;如果e是一个原子(一般为单个元素),执行判断 :e和x是否相等,是的话返回真,否则返回空          (t;;如果e既不是原子,即e是一个表,也不是空执行以下语句             (cond ((contain (car e) x) t);;用car取e 的表头继续递归调用contain判断,如果是真的包含x,返回真                   ((contain (cdr e) x) t);;用cdr取e 的表尾,继续递归调用contain判断,如果是真的包含x,返回真                  (t nil);;e的表尾和表头都不包含x,那么返回空             )          )    )) (defun sb(e s1);;e表示表达式,形如f(g(x)),s1表示单个代换对,即形如a/x.这个函数的作用是用单个代换对代换表达式,用列举的例子,代换的结果就是f(g(a))    (let (head tail)                                 (cond  ((null e) nil)                ((atom e) (if (equal e (second s1)) (first s1) e))                (t                     (setq head (sb (car e) s1))                                             (setq tail (sb (cdr e) s1))                      (cons head  tail)                )         )     ) )     (defun substitution(e s);;代换函数,e表示表达式集{f(x),f(g(y),x)},s 表示代换集,比如{a/x,b/y}。最后的代换结果是{f(a),f(g(b),a)}    (cond ((null s) e)          (t               (setq e (sb e (car s)))               (substitution e (cdr s))          )    ))(defun compose(s1 s2);;合成函数,s1,s2分别表示两个代换集s1={f(x)/z,g(f(y))/z} s2={a/x,b/y}.最后的结果是
 s1s2={f(a)/z,g(f(b))/y,a/x,b/y}  (let (new_s1)    (setq new_s1 (cp1 s1 s2)) ;;½«ÉÏs2×÷Óõ½s1µÄ·Ö×ÓÉÏ    (append new_s1 s2)))(defun cp1(s1 s2);;将代换集s2作用到代换集s1上,返回代换后的s1  (let (ti vi new_ti)    (cond ((null s1) '())          (t  (setq ti (caar s1))                     (setq vi (cdar s1))                    (setq new_ti (substitution ti s2))                (cons (cons new_ti vi) (cp1 (cdr s1) s2)))))) (defun unify(e1 e2);;合一函数,e1,e2都为表达式,如E1=p(x,y) E2=p(a,b) 最后返回的结果就是{a/x,b/y}.  (let  (bf  f1  f2  t1  t2  s1  s2  g1  g2)  (cond ((or (atom e1) (atom e2))         (when  (not (atom e1))  (setq bf e1) (setq e1 e2) (setq e2 bf))         (cond               ((equal e1 e2)                     '())               ((and (isvar e1) (contain e2 e1))       'fail)               ((isvar e1)                        (list (list e2 e1)))               ((isvar e2)                        (list (list e1 e2)))               (t                               'fail)) )        (t         (setq f1 (car e1)) (setq t1 (cdr e1))         (setq f2 (first e2)) (setq t2 (rest e2))         (setq s1 (unify f1 f2))         (cond ((equal s1 'fail)  'fail)               (t                   (setq g1 (substitution t1 s1))                  (setq g2 (substitution t2 s1))                  (setq s2 (unify g1 g2))                  (if  (equal s2 'fail)  'fail  (compose s1 s2)))))) ) )

函数写在新建的文件夹里并保存,使用listener之前先装载(load)

可以调用合一函数,也可以调用其他的函数,都是类如下的,不过调用的时候都需要将原表达式或者代换对转换为广义表的形式。

PS:以下的输入中 ' 表示原表,即对 ' 之后的整个表进行运算,否则编译器就会对表内的元素进行运算。

程序的输入如下,调用合一函数:


按回车之后产生如下结果:


0 0