V2 использует стандартную аккумулирующую переменную для выполнения хвостовой рекурсии:
loop ([0;1;2;3;4;5;6;7;8], []) ->
loop ([3;4;5;6;7;8], [(0,1,2)]) ->
loop ([6;7;8], [(3,4,5), (0,1,2)]) ->
loop ([], [(6,7,8), (3,4,5), (0,1,2)]) ->
[(6,7,8), (3,4,5), (0,1,2)]
V3 использует продолжение , или простым языком, функция накопления :
loop ([0;1;2;3;4;5;6;7;8], x->x) ->
loop ([3;4;5;6;7;8], x->(0;1;2)::x) ->
loop ([6;7;8], x->(3;4;5)::x) ->
loop ([], x->(6,7,8)::x) ->
[(6,7,8)] // x->(6,7,8)::x applies to []
->
[(3,4,5);(6,7,8)] // x->(3,4,5)::x applies to [(6,7,8)]
->
[(0,1,2);(3,4,5);(6,7,8)] // x->(0,1,2)::x applies to [(3,4,5);(6,7,8)]
Вы можете увидеть разницу между накапливающимися переменными и накапливающими функциями:
Использование накапливающихся переменных прекращается при последнем вызове, так как накапливающая переменная хранит ответ. Однако функция накопления по-прежнему выполняет некоторую возврат после последнего вызова. Следует отметить, что использование аккумулирующих функций действительно является хвостовой рекурсией, поскольку рекурсивный вызов loop t (fun ls -> accfun ((a,b,c)::ls))
на самом деле является последним оператором этой функции.
Кстати, код, который вы предоставили, является хорошим примером для демонстрации концепции рекурсивных функций хвоста. Один из способов понять этот пример кода - это работать с небольшими делами , как я делал на двух приведенных выше иллюстрациях. Поработав над некоторыми мелкими делами, вы поймете концепцию глубже.