構造再帰とテール再帰の間に違いはありますか、それとも両方同じですか?これらの再帰の両方で、再帰関数が元のアイテムのサブセットで呼び出されていることがわかります。
構造再帰とテール再帰の間に違いはありますか、それとも両方同じですか?これらの再帰の両方で、再帰関数が元のアイテムのサブセットで呼び出されていることがわかります。
回答:
構造的な再帰:再帰的な呼び出しは、構造的に小さい引数に対して行われます。
末尾再帰:再帰呼び出しは最後に発生します。
末尾の再帰をより小さな引数で呼び出す必要があるという要件はありません。実際、末尾再帰関数は、多くの場合、永久にループするように設計されています。たとえば、これはささいな末尾再帰です(あまり役に立ちませんが、末尾再帰です)。
def f(x):
return f(x+1)
実際にはもう少し注意する必要があります。関数にはいくつかの再帰呼び出しがあり、それらすべてが末尾再帰である必要はありません。
def g(x):
if x < 0:
return 42 # no recursive call
elif x < 20:
return 2 + g(x - 2) # not tail recursive (must add 2 after the call)
else:
return g(x - 3) # tail recursive
末尾再帰呼び出しについて話します。再帰呼び出しがすべて末尾再帰である関数は、末尾再帰関数と呼ばれます。
末尾再帰は、構造再帰の非常に単純なケースであり、問題の構造はリンクリストです。主に使用している言語では、このリストは文字通りコードに含まれていない可能性があります。むしろ、それは概念的な「関数の呼び出しのリスト」であり、その言語を使用して書かれたように表現することができない可能性がある概念です。Haskell(私の言語)では、末尾再帰関数呼び出しは、実際には、要素が文字どおり「関数の呼び出し」であるリテラルリストのシーケンスアクションで置き換えることができますが、これはおそらく関数型言語のものです。
構造的再帰は、他の(場合によっては複合)オブジェクトの複合として定義されたオブジェクトを操作する方法です。たとえば、バイナリツリーは2つのバイナリツリーへの参照を含むオブジェクトであるか、空です(したがって、再帰的に定義されたオブジェクトです)。自己参照的ではありませんが、t1とt2がペアである必要はありませんが、いくつかのタイプt1とt2の2つの値を含むペア(t1、t2)は構造的再帰を許可します。この再帰は次の形式を取ります
ペアのアクション=各要素の他のアクションの結果の組み合わせ
あまり深遠に聞こえません。
構造的な再帰は末尾再帰にならないことがよくありますが、あらゆる種類の再帰は末尾再帰として書き直すことができます(証明:元の再帰を実行すると、アクションは特定の順序で完了します。したがって、再帰これは、前に説明したように、末尾再帰である特定の一連のアクションを実行することと同じです)。
バイナリツリーまたは上記のペアの例のいずれかがこれを示しています。ただし、サブオブジェクトに再帰呼び出しを配置する場合、最後のアクションにできるのはそのうちの1つだけです。結果が何らかの方法(たとえば、加算)で結合されている場合、どちらもない可能性があります。Andrej Bauerが彼の回答で述べているように、これは、結果が変更されている限り、再帰呼び出しが1つだけでも発生する可能性があります。言い換えると、効果的にリンクされたリスト以外のすべてのタイプのオブジェクト(1つのサブオブジェクトだけが下にある)の場合、構造再帰は末尾再帰ではありません。
f x = (stuff defining x'); f x'
ように定義されたリンクリスト内のノードのシーケンスと同じであることはトートロジーのようl = modify f : l
です。私にとっては、用語の類似性だけではありませんでした。二分木に対する末尾再帰について、詳しく説明していただけませんか?線形化の事実については、最後から2番目の段落からしか考えることができません。
f (f x)
、の外部呼び出しが末尾再帰である場合があることに注意してくださいf
。それはリストに関するものだという見方にどのように適合しますか?:ここでは別の例ですf : (Int -> Int) -> (Int -> Int)
とf g 0 = g 42
してf g (n + 1) = f (f . g) n
。可能性は無限であり、いくつかは有用です。
f (f x)
if (c) then f a else f b == let x = if (c) then a else b in f x
f . g
f g = \n -> if n == 0 then g 42 else f (f . g) (n - 1)
f
f g = h where { h 0 = g 42; h n = f (f . g) (n - 1) }
、それを議論に取り入れれば、再帰関数であるかどうかにかかわらず、末尾が許容され、この用語は無意味になります。