Should I avoid tail recursion in Prolog and in general?

前端 未结 5 1744
后悔当初
后悔当初 2020-12-02 01:49

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 acc

5条回答
  •  一生所求
    2020-12-02 02:23

    O.P. said:

    But I have read that it is better to avoid [tail] 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?

    It is a fairly straightforward optimization to convert a tail-recursive construct into iteration (a loop). Since the tail (recursive) call is the last thing done, the stack frame can be reused in the recursive call, making the recursion, for all intents and purposes, a loop, by simply jumping to the beginning of the predicate/function/method/subroutine. Thus, a tail recursive predicate will not overflow the stack. Tail-recursive construct, with the optimization applied have the following benefits:

    • Slightly faster execution as new stack frames don't need to be allocated/freed; further, you get better locality of reference, so arguably less paging.
    • No upper bound on the depth of recursion.
    • No stack overflows.

    The possible downsides?

    • loss of useful stack trace. Not an issue if TRO is only applied in a release/optimized build and not in a debug build, but...
    • developers will write code that depends on TRO, which means that code will run fine with TRO applied will fail without TRO being applied. Which means that in the above case (TRO only in release/optimized builds), a functional change exists between release and debug builds, essentially meaning one's choice of compiler options generates two different programs from identical source code.

    This is not, of course, an issue, when the language standard demands tail recursion optimization.

    To quote Wikipedia:

    Tail calls are significant because they can be implemented without adding a new stack frame to the call stack. Most of the frame of the current procedure is not needed any more, and it can be replaced by the frame of the tail call, modified as appropriate (similar to overlay for processes, but for function calls). The program can then jump to the called subroutine. Producing such code instead of a standard call sequence is called tail call elimination, or tail call optimization.

    See also:

    • What Is Tail Call Optimization?
    • The Portland Pattern Repository on the matter
    • and even Microsoft's MSDN

    I've never understood why more languages don't implement tail recursion optimization

提交回复
热议问题