画像はインターネットより

最初、この文章を読むと、回りくどくて非常に分かりにくいと感じるでしょう。 実は、再帰を使って読むととても簡単になります: 再帰には終点(小さな鯉)が必要です。 再帰が終点に達するまで、関数は自身を繰り返し呼び出します。 明らかに、「私の小さな鯉」というフレーズを出力することが再帰の終了条件です。 これをコードで書くと、次のようになります:

#include <stdio.h> void Recursion(int depth){ printf(“抱着”); if (!depth) printf(“我的小鲤鱼”); else Recursion(–depth); printf(“的我”); } int main(){ printf(“吓得我抱起了\n”); Recursion(2); putchar(’\n’); }

私がこれまでに見つけた再帰の最も適切な例えは、辞書を引くことです。私たちが使う辞書自体が再帰的です。一つの単語を説明するために、より多くの単語が必要になります。ある単語を調べて、その説明の中にまだ分からない単語を見つけたとします。すると、あなたはその2番目の単語を調べ始めます。残念ながら、2番目の単語の説明にも分からない単語があり、3番目の単語を調べます。このように調べていき、完全に理解できる説明の単語にたどり着くまで続けます。その時点で再帰は終わりに達し、そこからあなたは後戻りを始め、以前に調べた各単語を一つずつ理解していき、最終的に、最初に調べた単語の意味を理解するのです。。。

矢印はプログラムの実際の実行ステップを表しています。

上の多くの回答を見ましたが、そのほとんどが再帰という現象を説明することに偏っており、なぜ再帰を使うのか、再帰の考え方とは一体何なのかについては説明されていません。先日ちょうど関連するものを読んだので、整理してみたいと思います。もし間違いがあれば、ご指摘いただけると幸いです。

1. 定義

Wiki [1]: Recursion is the process of repeating items in a self-similar way.

コンピュータ科学においては [2]:

再帰(英語:Recursion)、または遞迴とも訳され、数学とコンピュータ科学において、関数の定義の中に関数自身を使用する方法を指します。

英語のRecursionは語源的に分析すると「re- (再び)」+「curs- (来る、起こる)」であり、つまり繰り返し発生する、再び現れるという意味です。一方、対応する中国語の翻訳「递归」は、「递」(進む)+「归」(帰る)という2つの意味を表しています。この2つの意味こそが、再帰という考え方の真髄です。この点から見ると、中国語の翻訳の方がかえって意味をよく表していると言えます。

2. ループとの違い

上記のWikiの定義だけを見ると、一般的に言われる無限ループと非常によく似ているように見えます。それらの違いは何でしょうか?

再帰は静中の動であり、行きと帰りがある。 ループは動静一如であり、行きっぱなしで帰りがない。

例を挙げましょう。あなたに一本の鍵が与えられ、ドアの前に立っています。この鍵で何枚のドアを開けられるかと尋ねられます。

再帰:あなたは目の前のドアを開け、部屋の中にもう一つのドアがあるのを見つけます(このドアは前に開けたドアと同じ大きさかもしれません(静)、あるいは少し小さいかもしれません(動))。あなたはそちらへ歩いていき、手の中の鍵でそれも開けられることに気づきます。ドアを押し開けると、中にはまた別のドアがあります。あなたは開け続けます。。。何度か繰り返した後、あるドアを開けると、そこには部屋があるだけで、もうドアはありません。あなたは来た道を戻り始めます。部屋を一つ戻るごとに、数を数えます。入り口まで戻ったとき、あなたはその鍵で一体何枚のドアを開けたのか答えることができます。

ループ:あなたは目の前のドアを開け、部屋の中にもう一つのドアがあるのを見つけます(このドアは前に開けたドアと同じ大きさかもしれません(静)、あるいは少し小さいかもしれません(動))。あなたはそちらへ歩いていき、手の中の鍵でそれも開けられることに気づきます。ドアを押し開けると、中にはまた別のドアがあります(前のドアが同じなら、このドアも同じ。2番目のドアが1番目のドアより小さくなっていれば、このドアも2番目のドアより小さくなっている(動静一如、変化がないか、同じ変化をする))。あなたはこのドアを開け続けます。。。ずっとこのまま進んでいきます。入り口にいる人は、あなたが答えを教えに帰ってくるのを永遠に待つことになります。

3. 再帰の考え方

再帰とは、進んで(递去)、帰ってくる(归来)ことです。

具体的に言うと、なぜ「進む」ことができるのでしょうか? これは、再帰的な問題が、類似しているが少し異なる問題に対しても同じ解決策で答えられる必要があることを要求します(上記の例で言えば、一本の鍵で後続のドアの鍵も開けられること)。

なぜ「帰る」ことができるのでしょうか? これは、これらの問題が絶えず大きいものから小さいものへ、近いものから遠いものへと進む過程で、終点、臨界点、ベースライン、つまりそれ以上小さく、遠くへ進む必要がなくなる点が存在し、その点から元の道を通って原点に戻ることを要求します。

このブログ記事[3]の著者は次のようにまとめています:

再帰の基本的な考え方は、規模の大きな問題を、規模の小さな類似した部分問題に変換して解決することです。関数の実装において、大きな問題を解決する方法と小さな問題を解決する方法が同じであることが多いため、関数が自身を呼び出すという状況が生まれます。また、この問題解決関数は明確な終了条件を持たなければなりません。そうでなければ、無限再帰が発生してしまいます。

4. いつ再帰を使うべきか?

問題の定義自体が再帰的な形式である場合、再帰を使って解決するのが最も適しています。

コンピュータ科学を専攻する学生にとって最も馴染み深いのは、「」の定義でしょう[4,5]。その他にも、階乗やフィボナッチ数列[6]などの定義があります。再帰を使ってこれらの問題を解決すると、一見非常に「恐ろしく」見える問題も、わずか数行のコードで解決できることがよくあります。もちろん、再帰のパフォーマンス問題は別の話で、スタックの割り当てや関数呼び出しのコストは、実際のエンジニアリングの実践で考慮すべき点です。しかし、今は再帰の考え方について議論しているだけなので、一旦それらのことは置いておき、再帰の美しさを鑑賞しましょう。

—-以上、ネットユーザーの回答より

著作権表示

著者: MoeJue

リンク: https://ja.moejue.cn/posts/42/

ライセンス: クリエイティブ・コモンズ表示-非営利-継承4.0国際ライセンス

この作品は、クリエイティブ・コモンズ表示-非営利-継承4.0国際ライセンスに基づいてライセンスされています。

検索を開始

キーワードを入力して記事を検索

↑↓
ESC
⌘K ショートカット