我有一个问题是前一主题的后续内容, 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文档。
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
创造一个新的利弊, 汽车是对象-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返回该缺点的cdr。 如果x为零, cdr返回nil
测试表格按照它们的顺序一次评估一个 在参数列表中给出,直到找到测试形式 评估为真。
如果该子句中没有表单,则该值的主要值 test-form [ed:测试形式的第一个值,如果存在,则为nil cond表单返回没有值]。否则,表格 与此测试形式相关联的按顺序进行评估,左对齐 对,作为隐含的预测,以及最后返回的值 表格由cond表格返回。
一旦一个测试形式产生了真实,就没有其他测试形式 评估。如果没有测试形式产生true,则返回nil
请参阅 http://www.lispworks.com/documentation/HyperSpec/Body/m_cond.htm#cond 了解更多信息。
请参阅 http://www.lispworks.com/documentation/HyperSpec/Body/m_defun.htm#defun 了解更多信息。
t => T
(eq t 't) => T
(case 'b (a 1) (t 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的转换更小,更容易的原因。所要做的就是将一行代码上移一个或两个档次(本来可以是一个,就是一样)。