Skip to content
Accumulators
step 1/4

Reading — step 1 of 4

Learn

~1 min readPatterns and State

Pure functional recursion in Prolog can be slow because it builds the answer on the way OUT of the recursion. Accumulators flip this — pass partial results IN, build answer on the way IN.

Naive sum — O(n) but inefficient (no tail call):

sum([], 0).
sum([H|T], S) :- sum(T, S1), S is H + S1.

With accumulator — tail-recursive, much faster:

sum_acc([], Acc, Acc).
sum_acc([H|T], Acc, S) :-
    NewAcc is Acc + H,
    sum_acc(T, NewAcc, S).

%% Helper that hides the accumulator:
sum(L, S) :- sum_acc(L, 0, S).

The accumulator (Acc) carries the partial result. The base case copies it to the output. This is the same trick as left fold in functional languages.

Reverse with accumulator — naive reverse is O(n²):

%% Bad — quadratic
reverse_naive([], []).
reverse_naive([H|T], R) :-
    reverse_naive(T, RT),
    append(RT, [H], R).      %% append is O(n) per step

%% Good — linear
reverse_acc([], Acc, Acc).
reverse_acc([H|T], Acc, R) :-
    reverse_acc(T, [H|Acc], R).

reverse(L, R) :- reverse_acc(L, [], R).

Prepending ([H|Acc]) is O(1); appending (append(T, [H], R)) is O(n). The accumulator pattern lets you flip the building direction.

Most stdlib predicates (length/2, msort/2, sumlist/2) are implemented with accumulators internally. When writing recursive list code, prefer accumulators for performance.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…