再帰の削除-舞台裏の理論の調査


10

私はこのサイトに不慣れです。この質問は確かに研究レベルではありませんが、まあ。ソフトウェアエンジニアリングの背景は少しありますが、CSTheoryの背景はほとんどありませんが、魅力的です。長い話を簡単に言うと、この質問がこのサイトで受け入れられる場合は、次の詳細な回答をお願いします。

だから、私はすべての再帰プログラムに反復的な類似点があることを知っています。「システムスタック」に似たものを維持し、戻りアドレスなどの環境設定をプッシュすることによって、そのために提供される人気のある説明を理解しています。 。

もう少し具体的に、チェーンを呼び出す関数がある場合に、このステートメントをどのように証明するかを(正式に)確認したいと思います。さらに、F iがいくつかのF jを呼び出す可能性がある条件ステートメントがある場合はどうなりますか?つまり、潜在的な関数呼び出しグラフには、いくつかの強く関連するコンポーネントがあります。F0F1FiFi+1FnF0FiFj

これらの状況を、反復から反復へのコンバーターを使ってどのように処理できるかを知りたいです。そして、私が以前に言及した手で波打った説明は、この問題にとって本当に十分ですか?つまり、場合によっては、再帰の削除が簡単な場合があるのはなぜでしょうか。特に、バイナリツリーのプレオーダートラバーサルから再帰を削除することは本当に簡単です。これは標準的なインタビューの質問ですが、ポストオーダーの場合に再帰を削除することは常に私にとって悪夢です。

私が本当に求めているのは質問です2

(1)再帰を反復に変換できるというより正式な(納得できる?)証明は本当にありますか?

(2)この理論が本当にそこにあるのなら、なぜ、例えば、注文をより簡単に、注文を反復することがそれほど難しいと私が思うのはなぜですか?(私の限られた知性以外)


1
iteratizingという単語のように:)
Akash Kumar

私は完全に理解しているかどうかはわかりませんが、再帰がどこかで終了する場合は、実際に独自のスタックを使用してシステムスタックをシミュレートできます。パート(2)の場合、問題は計算の複雑さの点で変わりません。
singhsumit 2012

この質問は、まだ公開されていないコンピュータサイエンスのサイトに最適でした。2番目の質問については、なぜそれが難しいと思いますか?プロセスはほぼ同じでなければなりません。
ラファエル

皆さんのコメントに感謝します-かなりの読み物があると思います。
イタチうちは

@Raphael-ポストオーダーの反復処理が難しいと思う理由についてのコメント(私が実行できないことを除いて)。私は再帰の削除に関するいくつかの記事を読んでいて、末尾再帰関数と呼ばれるものに出くわしました。反復処理が簡単であることがわかります。私はまだこれが真実である理由を正式に理解していません。しかし、もう1つ追加すべきことがあります。ポストオーダーの反復には1つのスタックではなく2つのスタックが必要であると聞いていますが、詳細はわかりません。そして今、私は迷っています-なぜこれらの2つのトラバーサルモードのこの違いが?そして、なぜ末尾再帰は扱いやすいのですか?
イタチうちは

回答:


6

私が正しく理解していれば、他の関数呼び出しを含まない関数を自分自身に変換することは明らかです。

したがって、「コールチェーン」があると仮定します。さらに、F 1F n自体が再帰的でないと仮定すると(すでに変換済みであるため)、これらの呼び出しをすべてFの定義にインライン化できます。これにより、既に処理できる直接再帰的な関数になります。FF1FFF1FF

これは、一部の自体にFが発生する再帰呼び出しチェーンがある場合、つまりF jF F jの場合に失敗します。この場合、相互再帰があり、これを取り除くには別のトリックが必要です。アイデアは、両方の関数を同時に計算することです。たとえば、ささいな場合:FjFFjFFj

f(0) = a
f(n) = f'(g(n-1))

g(0) = b
g(n) = g'(f(n-1))

with f'およびg'非再帰関数(または少なくともfandから独立g)は

h(0) = (a,b)
h(n) = let (f,g) = h(n-1) in (f'(g), g'(f)) end

f(n) = let (f, _) = h(n) in f end
g(n) = let (_, g) = h(n) in g end

これは当然、関連する機能や複雑な機能にまで及びます。


お役に立てて嬉しいです。横にあるチェックマークをクリックして、お気に入りの回答を受け入れることを忘れないでください。
Raphael

1
Raphel、あなたのトリックは、両方の再帰関数が同じ型の引数を受け入れる場合にのみ機能します。異なる種類のタイプfg受け入れる場合は、より一般的なトリックが必要です。
Andrej Bauer

@AndrejBauer良い観察、私は完全にそれを逃した。私はラファエルのアプローチが本当に好きでしたが、あなたが一般的なケースで観察したように、おそらくいくつかの異なる考えが必要です。他に何か提案はありますか?
イタチうちは

@AndrejBauer真。私は再帰理論の観点から考えていました。ここには自然数しかありません。すべてを適切な方法でエンコードできるため、これで十分です。しかし、あなたの主張は実践にとって非常に有効です。私たちは書き換えなければならないだろうと思いますfし、g彼らは共通の入力エンコーディングを共有するまで、私たちは使用して1を持つことはできません(再帰スキームをと他の1)。2
ラファエル

まあ、それを行う方法についての私の答えを見てください。
Andrej Bauer

8

はい、再帰を反復に変えることができると信じる説得力のある理由があります。これは、すべてのコンパイラがソースコードを機械語に変換するときに行うことです。理論的には、Dave Clarkeの提案に従う必要があります。再帰を非再帰的なコードに変換する実際のコードを見たい場合machine.mlは、PL Zooの MiniML言語を見てください(実際にコードをloop実行する下部の関数は末尾再帰であり、簡単に実際のループに変換されます)。

もう一つ。MiniMLは相互再帰関数をサポートしていません。しかし、これは問題ではありません。関数間で相互再帰がある場合

f 2A 2B 2

f11B1
f22B2
fB

再帰は単一の再帰マップの観点から表現できます

f1++B1++B

8

SECDマシンを見るとよいでしょう。関数型言語(任意の言語であってもかまいません)は、スタックの引数の配置、新しい関数の "呼び出し"などを管理する一連の命令に変換され、すべて単純なループによって管理されます。
再帰呼び出しが実際に呼び出されることはありません。代わりに、呼び出される関数の本体の命令がスタックに配置されて実行されます。

関連するアプローチはCEKマシンです。

これらはどちらも長い間使用されてきたため、多くの作業が行われています。そしてもちろん、それらが機能することの証明があり、プログラムをSECD命令に「コンパイル」する手順はプログラムのサイズに比例します(プログラムについて考える必要はありません)。

私の答えの要点は、あなたが望むことをするための自動手順があるということです。残念ながら、この変換は、プログラマーがすぐに解釈できるものであるとは限りません。重要なのは、プログラムを反復化する場合、反復関数呼び出し(これは継続と呼ばれます)から戻ったときにプログラムが実行する必要があることをスタックに格納する必要があるということです。一部の関数(末尾再帰関数など)では、継続は簡単です。他の人にとっては、特に自分でエンコードする必要がある場合は、継続が非常に複雑になる可能性があります。


私はここで正直になります。すべての再帰プログラムを繰り返し処理する理由(および方法)を理解したいと思います。しかし、私は論文を一読するのは難しいと感じました-通常、私はそれらにアクセスできません。私が質問で話していた「ハンドウェーブ」の説明よりも深い理由が欲しいということです。しかし、私はまた、私にいくつかの新しい洞察を与える何かに満足しています-それはその本質的な詳細で完全な証拠である必要はありません
イタチうちは

[cntd]あるプログラムがある場合、その証明が気に入って、なぜ1つのプログラムを他のプログラムよりも簡単に反復処理できるのかを教えてください。しかし、ある意味では、再帰的反復コンバーターは、入力としてどの再帰的プログラムを使用しても機能するはずです。わかりませんが、そのようなコンバータを作成することは、停止の問題と同じくらい難しいかもしれませんね?私はここで推測しているだけですが、再帰から反復へのコンバーターが存在することを望みます。存在する場合は、さまざまな再帰プログラムを反復することの固有の複雑さを説明したいと思います。わかりませんが、質問を編集する必要がありますか?私の質問は明確ですか?
イタチうちは

@ItachiUchiha-私はあなたの問題が決定できないとは思いません。Andrej Bauerの答えを見てください。彼は、すべてのコンパイラがソースコードを機械語に変換するときにそれを行うと述べています。また、MiniM(a)l言語で再帰を非再帰に変換する実際のコードを確認できます。これは、再帰を「反復する」決定手順があることを明確に示しています。再帰の削除に固有の(概念的な)困難さ/複雑さについてはわかりません。私はこの質問をはっきりと理解していませんが、面白そうです。多分あなたはより良い返答を得るためにあなたの質問を編集することができます
Akash Kumar

私の答えの要点は、あなたが望むことをするための自動手順があるということです。残念ながら、この変換は、プログラマーがすぐに解釈できるものであるとは限りません。重要なのは、プログラムを反復化する場合、反復関数呼び出し(これは継続と呼ばれます)から戻ったときにプログラムが実行する必要があることをスタックに格納する必要があるということです。一部の関数(末尾再帰関数など)では、継続は簡単です。他の人にとっては、特に自分でエンコードする必要がある場合は、継続が非常に複雑になることがあります。
デイブクラーク

6

Q「再帰を反復に変換できるという、より正式な(納得のいく)証明は本当にありますか?」

:チューリングマシンのチューリング完全性:-)

冗談は別ですが、チューリングの同等のランダムアクセスストアドプログラム(RASP)マシンモデルは、実際のマイクロプロセッサの動作に近く、その命令セットには条件付きジャンプのみが含まれます(再帰なし)。コードを動的に自己修正できるため、サブルーチンと再帰呼び出しを実装する作業が簡単になります。

再帰的な反復変換」(Daveの回答またはGoogleのキーワードを参照)で多くの論文/記事を見つけることができると思いますが、おそらくあまり知られていない(そして実用的な)アプローチは、再帰アルゴリズムのハードウェア実装に関する最新の研究(ハードウェアに直接「コンパイル」されたVHDL言語を使用します)。たとえば、V.Sklyarovの論文「再帰アルゴリズムのFPGAベースの実装」を参照してください(この論文では、ハードウェアに再帰アルゴリズムを実装する新しい方法を提案しています。...データの並べ替えと圧縮領域での再帰アルゴリズムの2つの実用的なアプリケーションが研究されています。詳細....)。


1

ラムダをサポートする言語に精通している場合、1つの方法はCPS変換を調べることです。呼び出しスタック(および特に再帰)の使用を削除することは、まさにCPS変換が行うことです。プロシージャコールを含むプログラムをテールコールのみのプログラムに変換します(これらは反復構造であるgotosと考えることができます)。

CPS変換は、呼び出しスタックを従来の配列ベースのスタックに明示的に保持することと密接に関連していますが、呼び出しスタックは配列ではなく、リンクされたクロージャーで表されます。


0

私の意見では、この質問は計算の定義の起源にさかのぼり、教会のラムダ計算(再帰の概念を高度に捉えている)がチューリングマシンと同等であることが示され、含まれているその頃に厳密に証明されましたまだ使用されている用語「再帰的な言語/関数」。また、明らかにこれらの行に沿った後のキー参照は次のとおりです

Peter Landinの1965年の論文A Correspondence between ALGOL 60 and Church's Lambda-notationで指摘されているように、順次手続き型プログラミング言語は、手続き型の抽​​象化と手続き(サブプログラム)アプリケーションの基本的なメカニズムを提供するラムダ計算の観点から理解できます。

これに関するbkdの多くは、このwikipediaページのChurching -turingの論文にあります。正確な詳細はわかりませんが、ウィキペディアの記事では、ラムダ計算とチューリングマシンの間の同等性を最初に証明したのはロッサー(1939)であったことが示されているようです。多分/おそらく彼の論文は、(おそらく再帰的な)ラムダ呼び出しをTM構造に変換するためのスタックのようなメカニズムを持っていますか?

ロッサー、JB(1939)。「ゴデルの定理と教会の定理の証明の非公式な説明」。Journal of Symbolic Logic(The Journal of Symbolic Logic、Vol。4、No. 2)4(2):53–60。doi:10.2307 / 2269059。JSTOR 2269059。

もちろん、現在のLisp言語とバリアントSchemeの原則に興味がある人は、意図的にラムダ計算によく似ています。式評価のインタープリターコードを研究すると、ラムダ計算のチューリング完全性に関する論文に元々含まれていたアイデアが導き出されます。


1
Turing / lambdaの同等性の証明は、このペーパーの付録にあります。www.cs.virginia.edu/〜robins / Turing_Paper_1936.pdf
Radu GRIGore
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.