Rekurze je krásná a elegantní, ale má jednu temnou stránku. Žere paměť. Představte si, že počítáte faktoriál čísla 1 000 000. Váš program si musí pamatovat milion "otevřených" volání (milion talířů na sobě), než se dostane k výsledku. Většinou to skončí chybou Stack Overflow (Přetečení zásobníku). Program spadne. Jak to vyřešit? Musíme psát kód chytřeji, aby si Prolog nemusel nic pamatovat.
Koncová rekurze je speciální styl psaní pravidel, kde rekurzivní volání je to úplně poslední, co pravidlo udělá. Prolog v tomto případě umí použít trik zvaný Tail Call Optimization (TCO). Zahodí starou paměť, protože už ji nepotřebuje. Je to jako předání štafetového kolíku. Běžec předá kolík a může odejít z dráhy. Nemusí tam stát a čekat, až se ten druhý vrátí.
Klasická (špatná) rekurze:
fact(N, R) :-
N1 is N - 1,
fact(N1, R1), % Čekáme na výsledek R1...
R is N * R1. % ...abychom ho mohli vynásobit. Musíme si pamatovat N!
Koncová (dobrá) rekurze:
Použijeme pomocný predikát s akumulátorem. Na začátku je akumulátor 1.
% Hlavní pravidlo pro uživatele (obal)
fact(N, R) :- fact_acc(N, 1, R).
% Základní případ: Když jsme u 0, výsledek je to, co jsme nastřádali v Akumulátoru.
fact_acc(0, Acc, Acc).
% Rekurzivní krok
fact_acc(N, Acc, R) :-
N > 0,
NewAcc is Acc * N, % Vypočítáme mezivýsledek HNED TEĎ
N1 is N - 1,
fact_acc(N1, NewAcc, R). % Předáme ho dál. Nic si nemusíme pamatovat!
Jak to běží:
fact_acc(3, 1, R) -> Spočti 1*3=3. Zavolej fact_acc(2, 3, R). (Zapomeň na 3)fact_acc(2, 3, R) -> Spočti 3*2=6. Zavolej fact_acc(1, 6, R). (Zapomeň na 2)fact_acc(1, 6, R) -> Spočti 6*1=6. Zavolej fact_acc(0, 6, R). (Zapomeň na 1)fact_acc(0, 6, R) -> Konec. R = 6.Optimalizace pomocí koncové rekurze šetří paměť (zásobník neroste), ale vyžaduje trochu jiný způsob myšlení. Místo "až se vrátíš, tak to spočítám" říkáme "tady máš, co jsem zatím spočítal, a běž dál".
fact_acc)? Proč nestačí jen fact?delka_acc(Seznam, Akumulator, Vysledek).
delka_acc([], A, A).
delka_acc([_|T], A, R) :- A1 is A + 1, delka_acc(T, A1, R).
time(Goal). Např. time(fact(10000, X)).