どういうわけか、この関数を一定のスタックスペースを使用するように書き直す良い方法を考えるのに苦労しています。フィボナッチ関数を使用し、その特定の問題の特性を利用することにより、ツリー再帰のチートに関するほとんどのオンラインディスカッションが行われます。この「現実世界」(フィボナッチ数列よりも現実世界)での再帰の使用について何かアイデアはありますか?
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)))))
編集:コメントでリクエストして...
一般的な用語で言い換えると、Schemeを使用します-スタックスペースを消費したり、非自己呼び出しの末尾呼び出しの最適化を必要としないように、次の再帰パターンを書き直すにはどうすればよいですか?
(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の代数的特性に依存しない答えを探しているという点を理解するために、迷惑な名前を選びました。再帰を書き直したいだけです。
更新:
Rich Hickeyは、Clojureに明示的なトランポリン機能を追加してくれました。