По какой-то причине у меня возникают проблемы с поиском хорошего способа переписать эту функцию, чтобы она использовала постоянное пространство стека. Большинство онлайн-обсуждений чит-рекурсии дерева с использованием функции Фибоначчи и использованием свойств этой конкретной проблемы. У кого-нибудь есть идеи по поводу использования рекурсии в этом «реальном мире» (ну, более реальном, чем в серии Фибоначчи)?
Clojure - интересный случай, так как он не имеет оптимизации хвостового вызова, а только хвостовой рекурсии через специальную форму "recur". Это также настоятельно не рекомендует использовать изменяемое состояние. В нем много ленивых конструкций, включая tree-seq , но я не могу понять, как они могут помочь мне в этом случае. Кто-нибудь может поделиться некоторыми приемами, которые они использовали в C, Scheme, Haskell или других языках программирования?
(defn flatten [x]
(let [type (:type x)]
(cond (or (= type :NIL) (= type :TEXT))
x
(= type :CONCAT)
(doc-concat (flatten (:doc1 x))
(flatten (:doc2 x)))
(= type :NEST)
(doc-nest (:level x)
(flatten (:doc x)))
(= type :LINE)
(doc-text " ")
(= type :UNION)
(recur (:doc1 x)))))
редактировать: По запросу в комментариях ...
Перечислено в общих чертах и с использованием схемы - как переписать следующий шаблон рекурсии, чтобы он не занимал место в стеке или не требовал оптимизации хвостовых вызовов для несамостоятельных вызовов?
(define (frob x)
(cond ((foo? x)
x)
((bar? x)
(macerate (f x) (frob (g x))))
((thud? x)
(frobnicate (frob (g x))
(frob (h x))))))
Я выбрал назойливые имена, чтобы указать на то, что я ищу ответы, которые не основаны на алгебраических свойствах x, macerate, frobnicate, f, g или h. Я просто хочу переписать рекурсию.
UPDATE
Rich Hickey любезно добавил в Clojure явную функцию батута .