Я знаю, что это немного поздно, и ответ был уже принято. Тем не менее, я считаю, что хорошее пошаговое руководство по созданию чего-то хвоста рекурсивного может представлять интерес для ОП или для кого-либо еще в этом отношении. Вот несколько советов, которые, несомненно, помогли мне. Я собираюсь использовать простой пример, отличный от простого поколения, потому что, как утверждают другие, существуют лучшие способы генерации простых чисел.
Рассмотрите наивную реализацию функции count, которая создаст список целых чисел, отсчитывающих от n
. Эта версия не является хвостовой рекурсией так и для длинных списков вы столкнетесь исключение переполнения стека:
let rec countDown = function
| 0 -> []
| n -> n :: countDown (n - 1)
(* ^
|... the cons operator is in the tail position
as such it is evaluated last. this drags
stack frames through subsequent recursive
calls *)
Один из способов исправить это применить продолжение проходящего стиль с параметризованной функцией:
let countDown' n =
let rec countDown n k =
match n with
| 0 -> k [] (* v--- this is continuation passing style *)
| n -> countDown (n - 1) (fun ns -> n :: k ns)
(* ^
|... the recursive call is now in tail position *)
countDown n (fun ns -> ns)
(* ^
|... and we initialize k with the identity function *)
Тогда, реорганизовать эту параметризованную функцию в специализированное представление. Обратите внимание, что функция countDown'
фактически не подсчитывает. Это артефакт того, как продолжение строится при n > 0
, а затем оценивается, когда n = 0
. Если у вас есть что-то вроде первого примера, и вы не можете понять, как сделать его хвостом рекурсивным, то я предлагаю вам написать второй, а затем попытаться оптимизировать его, чтобы исключить параметр функции k
. Это, безусловно, улучшит читаемость. Это оптимизация второго примера:
let countDown'' n =
let rec countDown n ns =
match n with
| 0 -> List.rev ns (* reverse so we are actually counting down again *)
| n -> countDown (n - 1) (n :: ns)
countDown n []
Я не уверен, что он действительно беспокоится о получении простых чисел; Я думаю, он больше заинтересован в понимании рекурсии. –
"generatePrimes" очень хорошо. Но я не могу понять это прямо сейчас. Я изучу его позже. Спасибо Инь Чжу. – kev