I'm working through "Learn Prolog now" online book for fun.
I'm trying to write a predicate that goes through each member of a list and adds one to it, using accumulators. I have already done it easily without tail recursion.
addone([],[]).
addone([X|Xs],[Y|Ys]) :- Y is X+1, addone(Xs,Ys).
But I have read that it is better to avoid this type of recursion for performance reasons. Is this true? Is it considered 'good practice' to use tail recursion always? Will it be worth the effort to use accumulators to get into a good habit?
I have tried to change this example into using accumulators, but it reverses the list. How can I avoid this?
accAddOne([X|Xs],Acc,Result) :- Xnew is X+1, accAddOne(Xs,[Xnew|Acc],Result).
accAddOne([],A,A).
addone(List,Result) :- accAddOne(List,[],Result).
addone
is already fully tail-call-optimizable. It is tail recursive "modulo cons", and Prolog does have this optimization -- the new cons cell[Y|Ys]
is allocated first with the two "holes" in it (two as yet uninstantiated logvars,Y
andYs
), thenY
is instantiated inside the rule's body (byis/2
), and then the recursive call instantiates the logical variableYs
. There's thus no need to return from the recursive call into this rule's body. – Styles