1. ホーム
  2. recursion

[解決済み] 再帰性はそれ自体が特徴なのか?

2022-05-18 14:31:33

質問

...それとも、ただの練習ですか?

教授と口論になったので、これを聞いています。私は、授業で再帰を扱っていないことを理由に、関数を再帰的に呼び出したことで単位を失いました。 return とメソッドを学ぶことによって暗黙的に学んだというのが私の主張です。

誰かが決定的な答えを持っていると思うので、ここで質問しています。

例えば、次の2つの方法の違いは何でしょうか。

public static void a() {
    return a();
    }

public static void b() {
    return a();
    }

" 以外は。 a は永遠に続く" (実際のプログラムでは、無効な入力があった場合に再度プロンプトを表示するために正しく使用されています) 以外の基本的な違いはあるのでしょうか? ab ? 最適化されていないコンパイラにとって、これらはどのように異なって扱われるのでしょうか?

最終的にそれは、学習することによって return a() から b を学んだ。 return a() から a . そうでしたっけ?

どのように解決するのですか?

あなたの具体的な質問に答えます。いいえ、言語を学ぶという観点からは、再帰は機能ではありません。もしあなたの教授が、まだ教えていない機能を使っているとして、本当に点数を減点したのなら、それは間違っています。

行間を読むと、1 つの可能性は、再帰を使用することによって、彼のコースの学習成果であるはずの機能を使用することを避けたということです。たとえば、反復をまったく使用しなかったか、または for ループの両方を使用せずに forwhile . 課題があなたの能力をテストすることを目的としていることはよくあることで、もしあなたがそれを避けるなら、教授は単にその機能のために用意された点数をあなたに与えることができません。しかし、それが本当に点数を落とした原因であるならば、教授はこれを自分の学習経験として受け止めるべきです。もし特定の学習成果を示すことが課題の基準の一つであるならば、それは学生に明確に説明されるべきです。

とはいえ、私は他のコメントや回答のほとんどと同意見で、ここでは反復は再帰より良い選択です。いくつかの理由があり、他の人たちはある程度それらに触れていますが、その背後にある考えを完全に説明しているとは思えません。

スタック オーバーフロー

より明白なのは、スタックオーバーフローエラーが発生する危険性があることです。現実的には、ユーザーがスタック オーバーフローを実際に引き起こすために何度も間違った入力をしなければならないので、あなたが書いたメソッドは実際にそれを引き起こすことは非常にまれです。

しかし、心に留めておくべきことの 1 つは、メソッド自体だけでなく、コール チェーンでより高いまたはより低い他のメソッドもスタック上にあることです。このため、利用可能なスタックスペースを気軽に食い尽くすことは、どのメソッドにとってもかなり無礼な行為です。他のコードが不必要に多くのスタックスペースを使用している可能性があるため、コードを書くときに常に空きスタックスペースを気にする必要はありません。

これは、抽象化と呼ばれるソフトウェア設計におけるより一般的な原則の一部です。本質的に、あなたが DoThing() を呼び出すとき、あなたが気にしなければならないのは、Thing が実行されることだけです。の実装の詳細について心配する必要はありません。 どのように の実装の詳細を気にする必要はありません。なぜなら、コードのすべてのビットが、コール チェーン内の他のコードによって残されたスタックをどれだけ安全に想定できるかを気にしなければならないからです。

可読性

読みやすさ

もうひとつの理由は、読みやすさです。コードが目指すべき理想は、人間が読める文書であり、各行が何をしているかを簡単に記述していることです。この2つのアプローチを取ります。

private int getInput() {
    int input;
    do {
        input = promptForInput();
    } while (!inputIsValid(input))
    return input;
}

private int getInput() {
    int input = promptForInput();
    if(inputIsValid(input)) {
        return input;
    }
    return getInput();
}

はい、どちらもうまくいきますし、理解するのも簡単です。しかし、この2つのアプローチは英語でどのように表現するのでしょうか?私は次のようなものだと思います。

入力が有効であるまで入力を促し、それを返します

対して

入力を促し、入力が有効であればそれを返し、そうでなければ入力を取得し、その結果を代わりに返します

後者については、もう少し簡潔な表現を考えることができるかもしれませんが、実際にやろうとしていることを概念的に説明するには、最初のものの方がより正確であることが常に分かると思います。これは、再帰が 常に 読みにくいということではありません。木構造のトラバーサルのように、再帰が輝く状況では、再帰と別のアプローチを同じように並べて分析することができ、ほぼ確実に、再帰がより明確に自己記述的なコードを提供することがわかります。

単独では、これらの両方は小さなポイントです。これが本当にスタック オーバーフローにつながることはほとんどなく、可読性の向上はわずかです。しかし、どのようなプログラムもこのような多くの小さな決定の集合体であるため、単独ではあまり重要でないとしても、それらを正しく行うための原則を学ぶことは重要です。