【发布时间】:2012-12-15 07:42:33
【问题描述】:
我正在阅读“Learn Prolog now”在线图书以获取乐趣。
我正在尝试编写一个谓词,该谓词使用累加器遍历列表的每个成员并向其添加一个。我已经很容易做到了,没有尾递归。
addone([],[]).
addone([X|Xs],[Y|Ys]) :- Y is X+1, addone(Xs,Ys).
但我已经读到,出于性能原因,最好避免这种类型的递归。这是真的?总是使用尾递归是否被认为是“好习惯”?使用累加器来养成一个好习惯值得付出努力吗?
我试图将此示例更改为使用累加器,但它反转了列表。我怎样才能避免这种情况?
accAddOne([X|Xs],Acc,Result) :- Xnew is X+1, accAddOne(Xs,[Xnew|Acc],Result).
accAddOne([],A,A).
addone(List,Result) :- accAddOne(List,[],Result).
【问题讨论】:
-
addone已经完全可尾调用优化。它是tail recursive "modulo cons",Prolog 确实有这种优化——新的 cons 单元[Y|Ys]被分配 first ,其中有两个“洞”(两个尚未实例化的 logvar,Y和Ys),然后Y在规则主体内被实例化(由is/2),然后然后递归调用实例化逻辑变量Ys。因此,无需从递归调用返回到此规则的主体。 -
LPN!如今有一整章是通过示例来展示差异的。据我所知,Prolog 是尾递归优化的,因此它是可取的,因为它通过不后退有效地将 N 步转换为 N/2。 LPN:learnprolognow.org/… 第 5.3 章,2018 年 1 月。
-
IOW 第一个 sn-p 是最好的。 Prolog 在以自上而下的方式生产/处理/构建其列表方面已经是最优秀的了。劣质语言迫使您反向构建列表,然后将其反向,但那是因为它们劣质。自上而下是最好的。
标签: prolog tail-recursion accumulator tailrecursion-modulo-cons