Вам нужно использовать аккумулятор. Хотя можно было сделать что-то вроде этого:
list_length([] , 0 ).
list_length([_|Xs] , L ) :- list_length(Xs,N) , L is N+1 .
который будет рекурсивно проходить до конца списка, а затем, когда каждый вызов возвращается, добавляйте единицу к длине, пока он не вернется на верхний уровень с правильным результатом.
Проблема с этим подходом заключается в том, что каждая рекурсия помещает в стек новый кадр стека. Это означает, что у вас [в конечном итоге] закончится место в стеке, если список будет достаточно длинным.
Вместо этого используйте хвостовой рекурсивный посредник, например:
list_length(Xs,L) :- list_length(Xs,0,L) .
list_length( [] , L , L ) .
list_length( [_|Xs] , T , L ) :-
T1 is T+1 ,
list_length(Xs,T1,L)
.
Этот код заполняет рабочий предикат, который несет аккумулятор, заполненный нулем. При каждой рекурсии он создает новый аккумулятор, значение которого равно текущему значению +1. Когда достигается конец списка, значение аккумулятора объединяется с желаемый результат.
Механизм пролога достаточно умен (оптимизация рекурсии TRO / хвоста), чтобы видеть, что он может повторно использовать кадр стека при каждом вызове (поскольку ни один из локальных переменных не используется после рекурсивного вызова), таким образом аккуратно преобразовывая рекурсию в итерацию.
person
Nicholas Carey
schedule
07.10.2013