Нет завершающего условия в цикле. Чтобы исправить свой код:
let rec somme1 x =
if x <= 0
then 0
else if ((x mod 3) = 0) or ((x mod 5) = 0)
then x + (somme1 (x-1))
else somme1 (x-1);;
somme1 1000;;
Чтобы улучшить ваш код, вы должны сделать свою функцию хвостовой рекурсивной.
let rec somme2 x accum =
if x <= 0
then accum
else if ((x mod 3) = 0) or ((x mod 5) = 0)
then somme2 (x-1) (accum+x)
else somme2 (x-1) accum
somme2 1000 0;;
Разница между двумя версиями заключается в том, что в случае хвостовой рекурсии результаты рекурсивного вызова точно совпадают с результатами функции, поэтому не требуется сохранять промежуточное состояние для завершения вычисления после рекурсивная функция называется. Когда вы вызываете somme1 1000;;
, тогда, поскольку (1000 mod 5 == 0) or (1000 mod 3 == 0)
оценивает true
, вы получаете рекурсивный вызов 1000 + (somme1 999)
, для завершения которого требуется рекурсивный вызов 999 + (somme1 998)
. Компилятор должен хранить числа 1000
и 999
в стеке до тех пор, пока не завершится выполнение somme1
, чего не происходит (без условия завершения), поэтому ваш стек заполняется при попытке сохранить 1000 + (999 + (996 + (995 + ...
.
Это будет эквивалентно ((((0 + 1000) + 999) + 996) + 995) + ...
, но в этом случае нет никаких промежуточных значений, необходимых для работы с результатом рекурсивных вызовов (т. Е. Возвращаемое значение рекурсивного вызова совпадает с возвращаемым значением самой функции), поэтому дополнительное пространство в стеке не требуется. Вторая версия работает таким образом. Если бы у него была та же ошибка, что и у первого, он бы не исчерпал стек, а просто продолжал бы работать бесконечно. Это считается улучшением. : -)