web-dev-qa-db-ja.com

再帰関数の例

再帰関数を説明するプログラミング例を提案できますか? Fibonacci seriesTowersなどの通常の古い馬がありますハノイのですが、それ以外のものは楽しいでしょう。

34
Codeslayer

この図 は、実際のプログラミング言語ではなく英語ですが、プロセスを非技術的な方法で説明するのに役立ちます。

子供は眠れなかったので、母親は小さなカエルの話をしました。
眠れなかったので、カエルの母親は小さなクマの話をしました
眠ることができなかったので、クマの母親は小さなイタチについて話をしました
 ...眠りに落ちた。
 ...そして小さなクマは眠りに落ちました; 
。 ..そして小さなカエルは眠りに落ちた; 
 ...そして子供は眠りに落ちた。
119
ConroyP

理解再帰 にするには、まず 再帰 を理解する必要があります。

33
Ilya Ryzhenkov

再帰の経験則は、「各反復でタスクが2つ以上同様のタスクに分割される場合にのみ再帰を使用する」です。

したがって、フィボナッチは再帰アプリケーションの良い例ではありませんが、ハノイは良い例です。

そのため、再帰の良い例のほとんどは、さまざまなdisquisでのツリー走査です。

例:グラフトラバーサル-訪れたノードが再び訪れないという要件により、グラフは効果的にツリーになります(ツリーは単純なサイクルのない接続されたグラフです)

分割および征服アルゴリズム(クイックソート、マージソート)-「分割」の後の部分は子ノードを構成し、「征服」は親ノードから子ノードにエッジを構成します。

18
Kaerber

パリンドロームであるために文字列をテストするのはどうですか?

bool isPalindrome(char* s, int len)
{
  if(len < 2)
    return TRUE;
  else
    return s[0] == s[len-1] && isPalindrome(&s[1], len-2);
}

もちろん、ループでより効率的に行うことができます。

15
Kip
13
Martin Cote

もう1つの「通常の容疑者」は、 Quicksort とMergeSortです。

10
agnul

数学の世界から、 アッカーマン関数 があります。

Ackermann(m, n)
{
  if(m==0)
    return n+1;
  else if(m>0 && n==0)
    return Ackermann(m-1, 1);
  else if(m>0 && n>0)
    return Ackermann(m-1, Ackermann(m, n-1));
  else
    throw exception; //not defined for negative m or n
}

常に終了しますが、非常に小さな入力であっても非常に大きな結果を生成します。たとえば、Ackermann(4、2)は2を返します65536 − 3。

9
Kip

以下は、ファイルシステムの世界からの実用的な例です。このユーティリティは、指定されたディレクトリの下のファイルを再帰的にカウントします。 (理由は覚えていませんが、実際にはこのようなものが必要でした...)

_public static int countFiles(File f) {
    if (f.isFile()){
        return 1;
    }

    // Count children & recurse into subdirs:
    int count = 0;
    File[] files = f.listFiles();
    for (File fileOrDir : files) {
        count += countFiles(fileOrDir);
    }
    return count;
}
_

(in Java a File インスタンスは通常のファイルまたはディレクトリのいずれかを表すことに注意してください。このユーティリティはディレクトリをカウントから除外します。)

一般的な実世界の例は、たとえば Commons IO ライブラリのFileUtils.deleteDirectory()API docsource を参照してください。

6
Jonik

私の意見では、再帰は知っておくと良いですが、再帰を使用できるほとんどのソリューションは反復を使用しても実行でき、反復ははるかに効率的です。

これは、ネストされたツリー(ASP.NETやWinformsなど)でコントロールを見つける再帰的な方法です。

public Control FindControl(Control startControl, string id)
{
      if (startControl.Id == id)
           return startControl

      if (startControl.Children.Count > 0)
      {
           foreach (Control c in startControl.Children)
           {
                return FindControl(c, id);
           }
      }
      return null;
}
6
FlySwat

インタープリターのデザインパターン は、多くの人が再帰を見つけられないため、非常に良い例です。ウィキペディアの記事にリストされているサンプルコードは、これをどのように適用できるかをよく示しています。ただし、インタープリターパターンをまだ実装しているはるかに基本的なアプローチは、ネストされたリストのToString関数です。

class List {
    public List(params object[] items) {
        foreach (object o in items)
            this.Add(o);
    }

    // Most of the implementation omitted …
    public override string ToString() {
        var ret = new StringBuilder();
        ret.Append("( ");
        foreach (object o in this) {
            ret.Append(o);
            ret.Append(" ");
        }
        ret.Append(")");
        return ret.ToString();
    }
}

var lst = new List(1, 2, new List(3, 4), new List(new List(5), 6), 7);
Console.WriteLine(lst);
// yields:
// ( 1 2 ( 3 4 ) ( ( 5 ) 6 ) 7 )

(はい、Evalという関数を期待している場合、上記のコードでインタープリターパターンを見つけるのは簡単ではないことを知っていますが、実際には、インタープリターパターンは関数が何を呼び出すか、それが何をするかさえ教えてくれませんGoFブックには、上記のパターンの例として上記が明示的にリストされています。)

6
Konrad Rudolph

実際の例は、「部品表原価計算」問題です。

最終製品を製造する製造会社があるとします。各製品は、その部品のリストと、それらの部品を組み立てるのに必要な時間によって説明できます。たとえば、ケース、モーター、チャック、スイッチ、コードから手持ち式の電気ドリルを製造し、5分かかります。

1分あたりの標準的な人件費を考えると、各製品の製造にはどれくらいの費用がかかりますか?

ああ、ところで、一部の部品(コードなど)が購入されているので、コストを直接知っています。

しかし、実際には一部の部品を自社で製造しています。ハウジング、ステーター、ローター、シャフト、ベアリングからモーターを作成します。所要時間は15分です。

そして、スタンピングとワイヤーからステーターとローターを作ります...

したがって、最終製品のコストを決定することは、実際には、プロセス内のすべてのリストからパーツへの関係を表すツリーを走査することになります。それは再帰アルゴリズムでうまく表現されています。確かに反復的に行うこともできますが、コアとなるアイデアは日曜大工の簿記と混同されるため、何が起こっているのかは明確ではありません。

5
joel.neely

他の人がすでに言ったように、多くの標準的な再帰の例は学術的です。

私が過去に経験したいくつかの実用的な用途は次のとおりです。

1-ファイルシステムやレジストリなどのツリー構造のナビゲート

2-他のコンテナコントロール(パネルやグループボックスなど)を含むコンテナコントロールの操作

2
JosephStyons

私が知っている最も毛深い例は、Knuthの Man or Boy Test です。再帰だけでなく、ネストされた関数定義(クロージャー)、関数参照、および定数/関数の二元性(名前による呼び出し)のALGOL機能を使用します。

2
Hugh Allen

私の個人的なお気に入りは Binary Search です

編集:また、ツリートラバーサル。たとえば、フォルダーのファイル構造を調べます。

1
Geoff

Implementing Graphs by Guido van Rossumには、グラフ内の2つのノード間のパスを見つけるためのPythonの再帰関数があります。

1
sanxiyn

私のお気に入りのソート、 ソートのマージ

(アルゴリズムを覚えているのでお気に入り そして パフォーマンス的にはそれほど悪くないですか)

1
crashmstr
  • 階乗
  • ツリーの深さのトラバース(ファイルシステム、ゲームスペース、またはその他の場合)
0
Vinko Vrsalovic

昔々、小学生はLogoとTurtle Graphicsを使用して再帰を学びました。 http://en.wikipedia.org/wiki/Turtle_graphics

再帰は、徹底的な試行によるパズルの解決にも最適です。 「塗りつぶし」(Google it)と呼ばれる一種のパズルがあり、そこではクロスワードのようなグリッドと、単語がありますが、手がかりも番号付きの正方形もありません。私はかつてパズル出版社向けに再帰を使ってプログラムを書いて、既知の解決策がユニークであることを確認するためにパズルを解きました。

0
SeaDrive

文字列を逆にするのはどうですか?

void rev(string s) {
  if (!s.empty()) {
    rev(s[1..s.length]);
  }
  print(s[0]);
}

これを理解すると、再帰を理解するのに役立ちます。

0
Yuval F

メニューツリーを再帰的に生成するためにしばらく前にこのサイトに投稿したサンプルを次に示します。 再帰的な例

0

処理リスト のようなものはどうですか:

  • マップ(およびandmap、ormap)
  • fold(foldl、foldr)
  • フィルタ
  • 等...
0
pkaeding

再帰関数は、 再帰的に定義されたデータ型 を扱うのに最適です。

  • 自然数はゼロまたは別の自然数の後継です
  • リストは、空のリスト、または要素が前にある別のリストです
  • ツリーは、いくつかのデータと0個以上の他のサブツリーを持つノードです

等。

0
Bruno De Fraine