У нас есть две функции, которые вычисляют факториал заданного числа. Первая, !
, использует стиль аккумулятора. Второй, fact
, использует естественную рекурсию.
(define (! n0)
(local (;; accumulator is the product of all natural numbers in [n0, n)
(define (!-a n accumulator)
(cond
[(zero? n) accumulator]
[else (!-a (sub1 n) (* n accumulator))])))
(!-a n0 1)))
и
(define (fact n)
(cond
[(= 0 n) 1]
[else (* n (fact (- n 1)))]))
В нижней части раздела 31 HtDP указано, что рекурсивная версия часто бывает такой же быстрой, если не быстрее, чем версия аккумулятора, но не указывает Причины, почему. Я прочитал об этом, и кажется, что ответ "оптимизация/устранение хвоста" , но статья Википедии, похоже, не согласуется с тем, что говорит HtDP, на по меньшей мере, в отношении производительности. Почему это так?
На работе рекурсивный стиль работает быстрее. Дома стиль аккумулятора быстрее. Не существует ли общей эвристики для выбора того, какой стиль обычно предпочтительнее? Я понимаю, что накопительный стиль более эффективен с точки зрения памяти, но если мы ограничим обсуждение только работой, то, по крайней мере, мне кажется, что это лучший выбор.
Я подумал об этом немного дальше и должен был бы присоединиться к статье в Википедии о превосходстве рекурсии в стиле накопителя в общем случае. Мало того, что это уменьшает использование пространства стека/кучи, доступ к памяти всегда будет за доступом к регистру, и теперь можно сделать более очевидным теперь, когда многоядерный компьютер находится здесь. Тем не менее, HtDP доказывает, что фактическое тестирование требуется во всех случаях.