Lisp尾部递归的Prolog翻译

时间:2017-05-10 23:50:23

标签: prolog lisp common-lisp tail-recursion tailrecursion-modulo-cons

我有一个问题是前一主题的后续内容, Should I avoid tail recursion in Prolog and in general?

在上面的链接文章中,用户false 提供了这个代码示例和这个解释......

  回到20世纪70年代,主要的AI语言是LISP。而且   相应的定义应该是......

  (defun addone (xs)
    (cond ((null xs) nil)
      (t (cons (+ 1 (car xs))
           (addone (cdr xs))))))
     

...这不是直接尾递归的:原因是缺点:   在那个时代的实现中,它的参数首先被评估,   只有这样,利弊才能被执行。所以你要重写这个   指示(并反转结果列表)是可能的   优化技术。

     

然而,在Prolog中,您可以在知道之前创建缺点   实际值,归功于逻辑变量。这么多的节目都是   在LISP中没有尾递归,转换为尾递归程序   序言

     

这种影响仍然可以在许多Prolog中找到   教科书。

我的问题是:上面有什么好的Prolog翻译 LISP代码?

编辑:添加了运行中的lisp代码示例和 描述各种lisp函数的lisp文档。

行动中的addone示例

1 > (addone '(1 2 3))

(2 3 4)

2 > (addone '('()))

> Error: The value 'NIL is not of the expected type NUMBER.
> While executing: CCL::+-2, in process listener(1).
> Type :POP to abort, :R for a list of available restarts.
> Type :? for other options.

3 > (addone '(a b c))

> Error: The value A is not of the expected type NUMBER.
> While executing: CCL::+-2, in process listener(1).
> Type :POP to abort, :R for a list of available restarts.
> Type :? for other options.

3 > ^C

lisp功能的文档

cons object-1 object-2 =>缺点

创造一个新的利弊, 汽车是对象-1, 并且其cdr是object-2。

例子
  (cons 1 2) =>  (1 . 2)
  (cons 1 nil) =>  (1)
  (cons nil 2) =>  (NIL . 2)
  (cons nil nil) =>  (NIL)
  (cons 1 (cons 2 (cons 3 (cons 4 nil)))) =>  (1 2 3 4)
  (cons 'a 'b) =>  (A . B)
  (cons 'a (cons 'b (cons 'c '()))) =>  (A B C)
  (cons 'a '(b c d)) =>  (A B C D)

(车x)=>对象

如果x是缺点, 汽车返回那个利弊的汽车。 如果x为零, 汽车返回零。

(cdr x)=>对象

如果x是缺点, cdr返回该缺点的cdr。 如果x为零, cdr返回nil

cond {clause} * =>结果*

clause :: =(test-form form *)

测试表格按照它们的顺序一次评估一个 在参数列表中给出,直到找到测试形式 评估为真。

如果该子句中没有表单,则该值的主要值 test-form [ed:测试形式的第一个值,如果存在,则为nil cond表单返回没有值]。否则,表格 与此测试形式相关联的按顺序进行评估,左对齐 对,作为隐含的预测,以及最后返回的值 表格由cond表格返回。

一旦一个测试形式产生了真实,就没有其他测试形式 评估。如果没有测试形式产生true,则返回nil

请参阅 http://www.lispworks.com/documentation/HyperSpec/Body/m_cond.htm#cond 了解更多信息。

defun function-name lambda-list form * =>函数名

请参阅 http://www.lispworks.com/documentation/HyperSpec/Body/m_defun.htm#defun 了解更多信息。

t => Ť

t =>  T 
(eq t 't) =>  T
(case 'b (a 1) (t 2)) =>  2

2 个答案:

答案 0 :(得分:4)

这是给定Lisp算法的Prolog中的再现。请注意,Lisp是有效的,Lisp函数可以返回值。这不是Prolog中的情况,因此您需要两个参数。

直接实施,不是关系性的,将是:

addone([], []).
addone([H|T], [H1|T1]) :-
    H1 is H + 1,
    addone(T, T1).

请注意,第二个谓词子句头部的[H1|T1]参数对应于Lisp中的(cons H1 T1)

这也可以使用maplist来完成,它距离原始的Lisp实现稍微有些步骤,但是Lisp确实有列表映射函数,可以用来创建一个看起来更像这样的Lisp实现:

addone_element(X, X1) :- X1 is X + 1.
addone(List, List1) :- maplist(addone_element, List, List1).

在Prolog中,可以使用CLP(FD)使其更具关系性,这对于推理整数是有用的:

:- use_module(library(clpfd)).

addone([], []).
addone([H|T], [H1|T1]) :-
    H1 #= H + 1,
    addone(T, T1).

maplist版本:

addone_element(X, X1) :- X1 #= X + 1.
addone(List, List1) :- maplist(addone_element, List, List1).

答案 1 :(得分:1)

直接翻译:

n(ext)

(defun addone (xs)
    (cond ((null xs) nil)
          (t (cons (+ 1 (car xs))
                   (addone (cdr xs))))))

但是,变了,

addone( XS, RESULT) :-
   (   XS = [],              % null XS ? then:
       RESULT = []           % 
   ;
       XS = [CAR | CDR],     % else:
       R is 1 + CAR,         % calculate the two
       addone( CDR, S)       %          fields         % almost TR,
       RESULT = [R | S],     % and cons them up        % save for this cons
   ).

完全尾部递归

(defun addone (xs)
    (let ((result))
      (cond ((null xs) (setf result nil))
            (t         (setf result (cons (+ 1 (car xs))
                                          (addone (cdr xs))))))
      result))
=
(defun addone (xs)
    (let ((result))
      (cond ((null xs) (setf result nil))
            (t         (setf result (list nil))
                       (setf (car result) (+ 1 (car xs)))
                       (setf (cdr result) (addone (cdr xs)))))
      result))
=
(defun addone (xs &optional (result (list nil)))        ; head sentinel
      (cond ((null xs))
            (t         (setf (cdr  result) (list nil))
                       (setf (cadr result) (+ 1 (car xs)))
                       (addone (cdr xs) (cdr result))))    ; almost TR
      (cdr result))
=
(defun addone (xs &aux (result (list nil)))
    (labels ((addone (xs result)
              (cond ((null xs))
                    (t (setf (cdr  result) (list nil))
                       (setf (cadr result) (+ 1 (car xs)))
                       (addone (cdr xs) (cdr result))))))   ; fully TR
        (addone xs result))
    (cdr result))

使用Boxing / head哨兵,因此我们可以在Common Lisp中使用可设置的指针,但是在Prolog中则不需要-Prolog的逻辑变量可以直接设置(一次),命名为指针。

这也是Prolog的转换比Lisp的转换更小,更容易的原因。所要做的就是将一行代码上移一个或两个档次(本来可以是一个,就是一样)。