私の問題は、再帰に非常によく似た特定のスタイルのコードにありますが、それほどではありません。再帰とは、ウィキペディアを引用すると、「定義されている関数が独自の定義内に適用される関数を定義する方法」です。同様に、相互再帰は、定義している関数を直接的または間接的に適用する別の関数を適用します。
問題は、私が考えて扱っているコードが同じ関数を使用していないことです! 別の関数で (メソッドまたはクロージャーとして)同じコードを使用します。
ここでの問題は、私のコードは同じですが、関数は同じではないということです。次の基本的な相互再帰の例を見てください。
def is_even(x):
if x == 0:
return True
else:
return is_odd(x - 1)
def is_odd(x):
if x == 0:
return False
else:
return is_even(x - 1)
これはいくぶん直感的で、非常に明確に相互再帰的です。ただし、呼び出しごとに 1 回作成される内部関数として各関数をラップすると、わかりにくくなります。
def make_is_even():
def is_even(x):
if x == 0:
return True
else:
return make_is_odd()(x - 1)
return is_even
def make_is_odd():
def is_odd(x):
if x == 0:
return False
else:
return make_is_even()(x - 1)
return is_odd
def is_even2(x):
return make_is_even()(x)
def is_odd2(x):
return make_is_odd()(x)
暗黙的なメモ化などの最適化を無視すると、厳密には再帰的ではない一連の関数呼び出しが生成され、同じ関数を 2 回呼び出すことなく、さまざまな新しい関数を作成して呼び出すことができます。それにもかかわらず、これらの関数はすべて共通のテンプレートに従っており、何度も何度も作成された同じ関数です (おそらく異なる自由変数を使用して)。
繰り返しますが、クラスを使用した直接同等の実装を考え出すことができます (結局のところ、クラスは実際にはクロージャにすぎません ;)。[insert name here]のこのスタイルは、たとえばComposite Patternで使用されるため、これは特に重要です。違いは、Composite 設計パターンとほとんどの用途 (クロージャーの場合も含む) では、通常、インスタンスがその場で作成されないことです。それは今でも本質的に同じです。
class EvenChecker(object):
def check(self, x):
if x == 0:
return True
else:
return OddChecker().check(x - 1)
class OddChecker(object):
def check(self, x):
if x == 0:
return False
else:
return EvenChecker().check(x - 1)
def is_even3(x):
return EvenChecker().check(x)
def is_odd3(x):
return OddChecker().check(x)
今回はオブジェクト作成とメソッド呼び出しの連鎖ですが、原理は同じです。(実際には、Python がオブジェクトごとに単純なラッパーを定義し、それ自体が毎回まったく同じ関数を呼び出すという点で、少し異なることに注意してください。しかし、これは必ずしも私たちが知る必要があるものではありません。クラスとオブジェクトの他の実装についても真である必要があります.しかし、はい、厳密に言えば、相互に再帰的であるだけでなく...もっと何か、それは私が知りたい他のことです.)