Хорошо, позвольте мне попробовать хвостовую рекурсию для чайников
Если вы выполните то, что должно быть сделано с reverseList, вы получите
reverseList(List(1,2,3, 4))
reverseList(List(2,3,4):::List(1)
(reverseList(List(3,4):::List(2)):::List(1)
((reverseList(List(4):::List(3)):::List(2)):::List(1)
Nil:::List(4):::List(3):::List(2):::List(1)
List(4,3,2,1)
С rlRec у вас будет
rlRec(List(1,2,3,4), Nil)
rlRec(List(2,3,4), List(1))
rlREc(List(3,4), List(2,1))
rlRec(List(4), List(3,2,1))
rlRec(Nil, List(4,3,2,1))
List(4,3,2,1)
Разница в том, что в первом случае переписывание становится все длиннее.Вы должны помнить, что нужно сделать после того, как завершится последний рекурсивный вызов reverseList
: элементы, которые нужно добавить к результату.Стек используется, чтобы запомнить это.Когда это заходит слишком далеко, вы получаете переполнение стека.Напротив, с rlRec
перезапись все время имеет один и тот же размер.Когда последний rlRec
завершается, результат доступен.Больше нечего делать, нечего запоминать, нет необходимости в стеке.Ключ в том, что в rlRec
рекурсивный вызов - return rlRec(something else)
, в то время как в reverseList
это return f(reverseList(somethingElse))
, с f
beging _ ::: List(x)
.Вы должны помнить, что вам придется звонить f
(что подразумевает также запоминание x
) (возвращать не нужно в scala, просто добавлено для ясности. Также обратите внимание, что val a = recursiveCall(x); doSomethingElse()
- это то же самое, что и doSomethingElseWith(recursiveCall(x))
, поэтомуне хвостовой вызов)
Когда у вас есть рекурсивный хвостовой вызов
def f(x1,...., xn)
...
return f(y1, ...yn)
...
, на самом деле нет необходимости запоминать контекст первого f
, когда возвращается второй.Таким образом, его можно переписать
def f(x1....xn)
start:
...
x1 = y1, .... xn = yn
goto start
...
Это то, что делает компилятор, поэтому вы избегаете переполнения стека.
Конечно, функция f
должна где-то иметь возврат, который не является рекурсивным вызовом.Вот где завершится цикл, созданный goto start
, точно так же, как и там, где останавливается серия рекурсивных вызовов.