web-dev-qa-db-ja.com

再帰の理解度を向上させるためのリソース?

私は再帰が何であるかを知っています(パッテンがそれ自体の中で再発する場合、通常はブレイクアウト条件付きの後に、その行の1つでそれ自体を呼び出す関数です...)?私の問題は、新しい例を見ると、いつも最初は混乱しています。ループ、またはマッピング、ジッピング、ネスト、ポリモーフィックな呼び出しなどが見られる場合、それを見れば、何が起こっているのかがわかります。再帰的なコードを見るとき、私の思考プロセスは通常「wtf is this?」です。続いて「ああ、それは再帰的です」が続き、「うまくいくと言えば、うまくいくはずです」が続きます。

それで、この分野でスキルを高めるためのヒント/計画/リソースはありますか?再帰は奇妙な概念の一種なので、それに取り組む方法も同様に奇妙で自明ではないかもしれないと考えています。

13
Andrew M

シンプルなものから始め、鉛筆と紙でそれをたどります。深刻に。通常の反復よりも再帰を使用するとはるかに簡単に処理できるため、ツリートラバーサルアルゴリズムを開始するのに適しています。これは複雑な例である必要はありませんが、簡単で操作できるものです。

はい、奇妙で直感に反することもありますが、クリックすると「エウレカ!」と言うと、あなたはどうやってあなたが不思議に思うでしょうしませんでした前にそれを理解してください! ;)木は(IMO)再帰で理解するのが最も簡単な構造であり、鉛筆と紙を使用して簡単に操作できるため、木を提案しました。 ;)

The Little Lisperという本を使用して、Schemeを強くお勧めします。使い終わったら、will再帰を深く理解します。ほとんど保証されています。

5
jcomeau_ictx

私は間違いなくSICPをお勧めします。また、著者の紹介コースビデオ here ;も確認してください。彼らは信じられないほど心を開くものです。

プログラミングにそれほど厳密に関連していない別の道は、Gödel、Escher、Bach:HofstadterによるEternal Golden Braidを読むことです。いったんそれを通過すると、再帰は算術と同じくらい自然に見えます。また、P = nPであると確信し、思考マシンを構築することになりますが、これは、メリットと比較すると非常に小さい副作用です。

4
cbrandolino

基本的にそれは練習に帰着します...一般的な問題(ソート、検索、数学の問題など)を取り、単一の関数を複数回適用する場合にそれらの問題を解決できる方法を確認できるかどうかを確認します。

たとえば、クイックソートは、リスト内の要素を2つの半分に移動し、それらをそれぞれの半分に再び適用するという点で機能します。最初のソートが行われるとき、その時点で2つの半分がソートされることは心配されていません。むしろ、ピボット要素を取得し、その要素よりも小さいすべての要素を片側に配置し、すべての要素を反対側に配置します。これは、その時点で再帰的に自分自身を呼び出して2つの新しい小さなリストをソートする方法に意味がありますか?彼らもリストです。ただ小さい。しかし、それらはまだソートする必要があります。

再帰の背後にある力は、分割と征服の概念です。問題を繰り返し、より小さな問題に分割します。問題は本質的に同じですが、それよりも小さいものです。それを十分に行うと、最終的には残りの部分だけが既に解決されているポイントに到達し、ループから抜け出すだけで問題が解決します。 それらの例を検討してくださいあなたが言及するまであなたは言及しました理解それら。しばらく時間がかかるかもしれませんが、最終的には簡単になります。次に、他の問題を取り上げて、それらを解決するために再帰関数を作成してください。幸運を!

編集:私はまた、再帰の重要な要素は、停止できる関数の保証された能力であることを追加する必要があります。つまり、元の問題の内訳は継続的にsmallerを取得する必要があり、最終的には保証された停止点(新しいサブ問題が解決可能またはすでに解決済み)。

2
Kenneth

個人的に私はあなたの最善の策は練習を通してあると思います。

LOGOで再帰を学びました。 LISPを使用できます。これらの言語では再帰は自然です。それ以外の場合は、前に来たことに基づいて次のことを表現する数学的スイートとシリーズの研究に例えることができます。つまり、u(n + 1)= f(u(n))、または複数の変数と複数の依存関係、例えばu(n)= g(u(n-1)、u(n-2)、v(n)、v(n-1)); v(n)= h(u(n-1)、u(n-2)、v(n)、v(n-1))...

したがって、私の提案は、単純な(式では)標準的な再帰の「問題」を見つけて、選択した言語でそれらを実装することです。練習は、それらの「問題」を考え、読み、表現する方法を学ぶのに役立ちます。多くの場合、これらの問題のいくつかは反復によって表現できますが、再帰はそれらを解決するためのよりエレガントな方法である可能性があります。それらの1つは階乗数の計算です。

私が見つけたグラフィカルな「問題」は、見やすくします。コッホのフレーク、フィボナッチ、ドラゴンカーブ、フラクタル全般を調べてください。しかし、クイックソートアルゴリズムも見てください...

いくつかのプログラム(無限ループ、無限リソースの暫定的な使用)をクラッシュさせ、(予期しない結果を得るために)終了条件を誤って処理してから、すべてに取り掛かる必要があります。そして、それを手に入れても、それらの過ちを犯すことになるでしょう。

2
asoundmove

コンピュータプログラムの構造と解釈

これは、再帰だけでなく、さまざまな大学でのプログラミング全般に関する学習に使用されるtheの本です。それはあなたがより多くの経験を得て、あなたがそれをより多く読むほど、より多くの情報を生み出すそれらの基礎的な本の1つです。

2
dietbuddha

OCamlやHaskellのようなMLスタイルの関数型言語で遊んでみることをお勧めします。パターンマッチング構文reallyは、比較的複雑な再帰関数でさえ理解するのに役立ち、Schemeのifおよびcondステートメントよりもはるかに優れています。 (HaskellとSchemeを同時に学びました。)

コントラストの簡単な例を次に示します。

(define (fib n)
   (cond [(= n 0) 0]
         [(= n 1) 1]
         [else (+ (fib (- n 1)) (fib (- n 2)))]))

そしてパターンマッチングで:

fib 0 = 0
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)

この例では、実際には違いを正義化していません-関数のどちらのバージョンでも問題が発生したことはありません。これは、2つのオプションがどのように見えるかを示すためだけのものです。リストやツリーなどを使用して、はるかに複雑な関数に到達すると、その違いはさらに顕著になります。

Haskellは構文が非常にシンプルな単純な言語であるため、特にお勧めします。 corecursionのようなより高度なアイデアを扱うのもはるかに簡単になります。

fibs = 0 : 1 : zipWith (+) fibs (drop 1 fibs)
fib n = fibs !! n

(上記のコードはHaskellで少し遊ぶまで理解できませんが、基本的には魔法のようです:P)。Schemeのストリームでも同じことができますが、Haskellの方がはるかに自然です。

0
Tikhon Jelvis

絶版ですが、もし見つけられれば、リチャード・ローレンツの「再帰的アルゴリズム」は再帰に他なりません。再帰の基本と特定の再帰アルゴリズムについて説明します。

例はパスカルですが、言語の選択が煩わしいほど大きくはありません。

0
Wayne Conrad