whileループは本質的に再帰ですか?


37

whileループが本質的に再帰かどうか疑問に思いましたか?

なぜなら、whileループは最後に自分自身を呼び出す関数とみなすことができるからだと思います。再帰ではない場合、違いは何ですか?



13
はい、再帰を反復に、またはその逆に変換できます。それは、それらが同じという意味ではなく、単に同じ機能を持っているだけです。再帰がより自然な場合があり、反復がより自然な場合があります。
ポリノーム

18
@MooingDuck帰納法により、再帰は反復として記述でき、その逆も可能であることを証明できます。はい、見た目は大きく異なりますが、それでもできます。
ポリノーム

6
何が本質的に同じ平均値をここに?プログラミングでは、再帰を使用するとは、反復(ループ)とは異なる特定のことを意味します。CSでは、物事の理論的な数学の側面に近づくと、これらの事柄は少し異なることを意味し始めます。
ハイド

3
@MooingDuck再帰から反復への変換は、実際には非常に簡単です。関数呼び出しパラメーターのスタックと関数呼び出しの結果のスタックを保持するだけです。再帰呼び出しを置き換えるには、呼び出しスタックにパラメーターを追加します。スタックの処理がすべてアルゴリズムの構造を少し壊すことは確かですが、これを理解すると、コードが同じことをすることは非常に簡単にわかります。基本的に、再帰的な定義で暗黙的な呼び出しスタックを明示的に記述します。
バクリウ

回答:


116

ループは再帰ではありません。実際、それらは反対のメカニズムの最も重要な例です:繰り返し

再帰のポイントは、処理の1つの要素がそれ自体の別のインスタンスを呼び出すことです。ループ制御機構は、単に開始点にジャンプして戻ります。

コード内をジャンプして、別のコードブロックを呼び出すことは、異なる操作です。たとえば、ループの先頭にジャンプしても、ループ制御変数の値はジャンプ前と同じままです。ただし、現在のルーチンの別のインスタンスを呼び出すと、新しいインスタンスには、そのすべての変数の新しい無関係なコピーが含まれます。事実上、1つの変数は、処理の最初のレベルで1つの値を持つことができ、より低いレベルで別の値を持つことができます。

この機能は、多くの再帰アルゴリズムが機能するために重要です。これが、すべての値を追跡する呼び出されたフレームのスタックを管理せずに、反復を介して再帰をエミュレートできない理由です。


10
@Giorgioそれは本当かもしれませんが、それは答えがなされなかった主張に対するコメントです。この回答には「任意」は存在せず、意味を大幅に変更します。
hvd

12
@hvd原則として、末尾再帰は他のすべての再帰と同じです。インテリジェントコンパイラは、実際の「新しいスタックフレームの作成」部分を最適化することにより、生成されたコードがループに非常に似ているようにしますが、ここで説明する概念はソースコードレベルに適用されます。私はアルゴリズムが持つフォームを検討し、ソースコードとして、私はまだ再帰それを呼び出すと思いますので、重要なことを
キリアンFoth

15
@Giorgio「これがまさに再帰の動作です。新しい引数で自分自身を呼び出します」-呼び出しを除きます。そして議論。
ホッブズ

12
@Giorgioあなたはここで最も異なる言葉の定義を使用しています。ご存知のように、言葉はコミュニケーションの基礎です。これはプログラマであり、CS Stack Exchangeではありません。「引数」、「呼び出し」、「関数」などの言葉をあなたが提案する方法で使用した場合、実際のコードについて議論することは不可能です。
ハイド

6
@Giorgio私は抽象的な概念を見ています。繰り返す概念とループする概念があります。それらは異なる概念です。Hobbsは、引数がなく、呼び出しがないという100%正しいです。それらは根本的にも抽象的にも異なります。そして、それは彼らがさまざまな問題を解決するからです。一方、あなたは唯一のツールが再帰である場合にループを実装する方法を検討しています。皮肉なことに、あなたの方法論が本当に再評価を必要とする方法であるとき、実装について考えるのをやめて、概念を見始めるようにHobbsに言っています。
corsiKa

37

Xが本質的にYであると言うのは、Xを表現していることを念頭に置いた(正式な)システムがある場合にのみ意味がありますwhile。レジスタマシンの観点から定義する場合、おそらく定義しません。

どちらの場合でも、whileループが含まれているという理由だけで関数を再帰的に呼び出すと、人々はおそらくあなたを理解しないでしょう。

*おそらく間接的にのみですが、たとえばを使用して定義する場合fold


4
公平を期すために、この関数はどの定義でも再帰的ではありません。ループという再帰的な要素が含まれているだけです。
ルアーン

@Luaan:確かにそうですが、whileコンストラクトを持つ言語では、再帰性は一般に関数のプロパティであるため、このコンテキストで「再帰的」と表現するものは考えられません。
アントンゴロフ

36

これはあなたの視点に依存します。

計算可能性理論を見ると、反復と再帰は等しく表現力があります。これが意味することは、何かを計算する関数を書くことができるということであり、それを再帰的または反復的に行うかどうかは関係なく、両方のアプローチを選択することができます。反復的に計算できない再帰的に計算できるものはありません逆も同様です)(プログラムの内部動作は異なる場合があります)。

多くのプログラミング言語では、再帰と反復を同じように扱わず、正当な理由があります。通常、再帰とは、言語/コンパイラが呼び出しスタックを処理することを意味し、反復とは、自分でスタック処理を行う必要がある場合があることを意味します。

ただし、ループ(for、while)のようなものが実際には再帰の構文糖のみであり、そのように舞台裏で実装される言語、特に関数型言語があります。関数型言語ではこれが望ましい場合が多くあります。通常、ループは他の方法ではループの概念を持たないため、実用的な理由はほとんどありませんが、ループを追加すると計算が複雑になります。

いいえ、本質的に同じではありません。それらは同じように表現力があり、繰り返し計算することはできませんし、再帰的に計算することはできませんが、一般的な場合はそれについてです(教会チューリングの論文による)。

ここで再帰プログラムについて話していることに注意してください。データ構造(ツリーなど)には、再帰の他の形式があります。


あなたからそれを見れば実装の観点、そして再帰と反復はほとんど同じではありません。再帰は、呼び出しごとに新しいスタックフレームを作成します。再帰のすべてのステップは自己完結型であり、呼び出し先(それ自体)から計算の引数を取得します。

一方、ループは呼び出しフレームを作成しません。彼らにとって、コンテキストは各ステップで保持されません。ループの場合、プログラムはループ条件が失敗するまでループの開始点にジャンプして戻るだけです。

これは、実世界でかなり根本的な違いを生む可能性があるため、知ることが非常に重要です。再帰では、呼び出しごとにコンテキスト全体を保存する必要があります。反復では、メモリ内の変数と保存場所を正確に制御できます。

そのように見ると、ほとんどの言語で反復と再帰が根本的に異なり、プロパティが異なることがすぐにわかります。状況によっては、一部のプロパティが他のプロパティよりも望ましい場合があります。

再帰を使用すると、プログラムをよりシンプルで簡単にテストおよび証明できます。通常、再帰を反復に変換すると、コードがより複雑になり、失敗の可能性が高くなります。一方、反復に変換して呼び出しスタックフレームの量を減らすと、必要なメモリを大幅に節約できます。


ローカル変数と再帰があるが配列がない言語は、ローカル変数と配列がない反復言語では実行できないタスクを実行できました。たとえば、入力に長さが不明な英数字の文字列が含まれ、その後に空白、元の文字列の文字が逆の順序で続くかどうかを報告します。
-supercat

3
言語が完全に調整されている限り、可能です。たとえば、配列は(二重に)リンクされたリストで簡単に置き換えることができます。反復または再帰、およびそれらが等しいかどうかについて話すことは、2つのチューリング完全言語を比較する場合にのみ意味があります。
ポリグノーム

単純な静的変数または自動変数以外、つまりチューリング完全ではないことを意味していました。純粋に反復的な言語は、単純な決定論的有限オートマトンを介して達成できるタスクに限定され、再帰言語は、少なくともプッシュダウンの決定論的有限オートマトンを必要とするタスクを実行する機能を追加します。
スーパーキャット

1
言語が完全ではない場合、そもそも無意味です。DFAは、任意の反復も再帰もできません。
ポリノーム

2
実際にチューリング完全な実装はありません。チューリング完全ではない言語は、多くの目的に役立ちます。有限範囲の有限数の変数で実行できるプログラムはすべて、DFAに対応できます。DFAでは、可能なすべての値の組み合わせが離散状態です。
スーパーキャット

12

違いは、暗黙的なスタックとセマンティックです。

「最後に自分自身を呼び出す」whileループには、完了時にクロールするスタックがありません。最後の反復で、終了時の状態を設定します。

ただし、以前に行われた作業の状態を記憶するこの暗黙的なスタックなしでは、再帰を行うことはできません。

スタックへのアクセスを明示的に許可した場合、反復で再帰問題を解決できることは事実です。しかし、そのようにするのは同じではありません。

意味の違いは、再帰コードを見ると、反復コードとはまったく異なる方法でアイデアが伝わるという事実に関係しています。反復コードは、一度に1ステップずつ処理を行います。以前の状態を受け入れ、次の状態を作成するためにのみ機能します。

再帰コードは問題をフラクタルに分割します。この小さな部分はその大きな部分のように見えるので、この少しだけでも同じようにできます。問題について考えるのは別の方法です。それは非常に強力で、慣れるのに時間がかかります。多くは数行で言えます。スタックにアクセスできる場合でも、whileループからそれを取得することはできません。


5
「暗黙のスタック」は誤解を招くと思います。再帰は、実装の詳細ではなく、言語のセマンティクスの一部です。(付与されたほとんどの再帰サポート言語はコールスタックを使用しますが、第一に、いくつかのそのような言語は使用しない言語で、第二に、すべての再帰呼び出しが必ずしもコールスタックに追加されるわけではありません。末尾呼び出しの削除。)通常/簡単な実装を理解することは、抽象化のハンドルを取得するのに有用であることができますが、それは全体の話だという考えに自分自身をだましてはいけません。
-ruakh

2
@ruakh末尾呼び出しの除去を使用して定数空間で実行される関数は、実際にはループであると主張します。ここで、スタックは実装の詳細ではなく、さまざまなレベルの再帰に対してさまざまな状態を蓄積できる抽象化です。
チンバリ

@ruakh:再帰を反復ループに変換できない限り、単一の再帰呼び出し内の状態は暗黙的なスタックに格納されます。末尾呼び出しの削除実装の詳細であり、関数を末尾再帰に再編成する場合に注意する必要があります。また、「そのような言語はほとんどありません」 -再帰呼び出しにスタックを必要としない言語の例を提供できますか?
グルー

1
@Groo:en.wikipedia.org/wiki/Continuation-passing_styleを参照してください。
-ruakh

@ruakh:CPSはそれ自体で同じ暗黙のスタックを作成するため、テールコールの除去に依存して意味をなす必要があります(構築方法により簡単になります)。あなたがリンクしたウィキペディアの記事でさえ同じことを言っています:末尾呼び出し最適化(TCO)なしでCPSを使用すると、再帰中に構築された継続だけでなく呼び出しスタックも成長する可能性があります
グルー

7

それはすべて、用語の本質的な使用にかかっています。プログラミング言語レベルでは、構文と意味が異なり、パフォーマンスとメモリ使用量がまったく異なります。しかし、理論を深く掘り下げると、互いの観点から定義できるため、理論的な意味で「同じ」になります。

本当の質問は次のとおりです。反復(ループ)と再帰を区別するのはいつ意味がありますか。また、いつそれを同じものと考えるのが便利ですか。答えは、(数学的な証明を書くのではなく)実際にプログラミングするときは、反復と再帰を区別することが重要だということです。

再帰は、新しいスタックフレーム、つまり、呼び出しごとにローカル変数の新しいセットを作成します。これにはオーバーヘッドがあり、スタック上のスペースを占有します。つまり、十分な深さの再帰がスタックをオーバーフローさせ、プログラムがクラッシュする可能性があります。一方、反復は既存の変数を変更するだけなので、一般に高速であり、一定量のメモリしか消費しません。したがって、これは開発者にとって非常に重要な違いです!

末尾呼び出し再帰を使用する言語(通常は関数型言語)では、コンパイラは、一定量のメモリのみを使用するように再帰呼び出しを最適化できる場合があります。これらの言語では、重要な区別は反復と再帰ではなく、非末尾呼び出し再帰バージョンの末尾呼び出し再帰と繰り返しです。

結論:違いを伝えることができなければ、プログラムがクラッシュします。


3

whileループは再帰の一種です。たとえば、この質問に対する受け入れられた答えを参照してください。それらは、計算可能性理論のμ演算子に対応します(たとえば、こちらを参照)。

for数値の範囲、有限コレクション、配列などで反復するループのすべてのバリエーションは、プリミティブな再帰に対応します。たとえばhereおよびhereを参照してください。そのノートforC、C ++、Java(登録商標)のループなどが、実際のための糖衣構文であるwhileループ、従ってそれはプリミティブ再帰に対応していません。Pascal forループは、プリミティブな再帰の例です。

重要な違いは、プリミティブな再帰は常に終了するのに対して、一般化された再帰(whileループ)は終了しない可能性があることです。

編集

コメントおよびその他の回答に関するいくつかの説明。「事物がそれ自体またはそのタイプに関して定義されると、再帰が発生します。」(ウィキペディアを参照)。そう、

whileループは本質的に再帰ですか?

whileループ自体を定義できるので

while p do c := if p then (c; while p do c))

そうはいwhileループは再帰の形式です。再帰関数は、再帰の別の形式です(再帰定義の別の例)。リストとツリーは、再帰の別の形式です。

多くの回答とコメントで暗黙的に想定されている別の質問は

whileループと再帰関数は同等ですか?

この質問に対する答えは「いいえ」です。whileループは末尾再帰関数に対応します。ループによってアクセスされる変数は、暗黙の再帰関数の引数に対応しますが、他の人が指摘したように、非末尾再帰関数while追加のスタックを使用せずにループでモデル化することはできません。

したがって、「whileループは再帰の一種」であるという事実は、「一部の再帰関数はwhileループで表現できない」という事実と矛盾しません。


2
@morbidCode:原始再帰とμ再帰は、計算可能性理論などで研究されている特定の制限(またはその欠如)を伴う再帰の形式です。FOR結局のところ、ループだけの言語は、すべてのプリミティブな再帰関数をWHILE正確に計算でき、ループだけの言語は、すべてのµ再帰関数を正確に計算できます(そして、µ再帰関数は、チューリングマシンで計算できます)。または、短くするために:原始再帰とμ再帰は、数学/計算可能性理論の技術用語です。
ヨルグWミットタグ

2
「再帰」はそれ自体を呼び出す関数を暗示し、その結果、現在の実行状態がスタックなどにプッシュされると思いました。したがって、ほとんどのマシンでは、再帰できるレベルの数に実際的な制限があります。ループは内部的に「JMP」のようなものを使用し、スタックを使用しないため、ループにはそのような制限はありません。ちょうど私の理解、間違っている可能性があります。
ジェイ

13
この答えは、「再帰的」という言葉に対して、OPが使用していたものとはまったく異なる定義を使用しているため、非常に誤解を招く可能性があります。
Mooingダック

2
@DavidGrinberg:引用:「C、C ++、Java forループはプリミティブ再帰の例ではありません。プリミティブ再帰とは、ループを開始する前に最大反復回数/再帰深度が固定されることを意味します。」ジョルジオは、計算可能性理論のプリミティブについて話している。プログラミング言語とは無関係。
Mooingダック

3
Mooing Duckに同意する必要があります。計算可能性理論は理論的なCSでは興味深いかもしれませんが、OPがプログラミング言語の概念について話していたことに誰もが同意すると思います。
Voo

2

末尾呼び出し(または末尾再帰呼び出し)は、正確に(任意のプッシュなし「の引数を持つ後藤」として実装され、追加のコールフレームを呼び出しスタック)と、いくつかの関数型言語(特にOCamlの)でループの通常の方法です。

したがって、whileループ(それらが存在する言語では)は、その本体(またはそのヘッドテスト)への末尾呼び出しで終わると見なすことができます。

同様に、通常の(末尾呼び出しではない)再帰呼び出しは、ループ(何らかのスタックを使用)によってシミュレートできます。

継続継続渡しスタイルについてもお読みください。

したがって、「再帰」と「反復」は完全に同等です。


1

再帰と無制限のwhileループの両方が計算表現力の点で同等であることは事実です。つまり、再帰的に記述されたプログラムは、代わりにループを使用して同等のプログラムに書き換えることができ、逆も同様です。どちらのアプローチもチューリング完全であり、いずれかの計算可能な関数を計算するために使用できます。

プログラミングに関する基本的な違いは、再帰により、呼び出しスタックに格納されるデータを利用できることです。これを説明するために、ループまたは再帰のいずれかを使用して、一重リンクリストの要素を印刷するとします。サンプルコードにはCを使用します。

 typedef struct List List;
 struct List
 {
     List* next;
     int element;
 };

 void print_list_loop(List* l)
 {
     List* it = l;
     while(it != NULL)
     {
          printf("Element: %d\n", it->element);
          it = it->next;
     }
 }

 void print_list_rec(List* l)
 {
      if(l == NULL) return;
      printf("Element: %d\n", l->element);
      print_list_rec(l->next);
 }

シンプルでしょ?次に、少し変更を加えます。リストを逆順に印刷します。

再帰的なバリアントの場合、これは元の関数に対するほとんど些細な変更です。

void print_list_reverse_rec(List* l)
{
    if (l == NULL) return;
    print_list_reverse_rec(l->next);
    printf("Element: %d\n", l->element);
}

ただし、ループ機能には問題があります。私たちのリストは一重にリンクされているため、前方にのみ移動できます。ただし、逆方向に印刷するため、最後の要素の印刷を開始する必要があります。最後の要素に到達すると、2番目から2番目の要素に戻ることはできなくなります。

そのため、多くの再走査を行うか、訪問した要素を追跡して効率的に印刷できる補助データ構造を構築する必要があります。

なぜ再帰にこの問題がないのですか?再帰では、補助データ構造がすでにあるため、関数呼び出しスタックです。

再帰により、以前の再帰呼び出しの呼び出しに戻ることができ、その呼び出しのすべてのローカル変数と状態はそのままであるため、反復的なケースでモデル化するのが面倒な柔軟性が得られます。


1
もちろん、2番目の再帰関数は末尾再帰ではありません。TCOを使用してスタックを再利用できないため、スペースを最適化するのははるかに困難です。二重にリンクされたリストを実装すると、両方のアルゴリズムがどちらの方法でも簡単になりますが、要素ごとのポインタ/参照のスペースが犠牲になります。
Baldrickk

@Baldrickkテール再帰の面白いところは、ループバージョンがコールスタックに状態を保存する機能が再び削除されるため、ループバージョンに非常に近いバージョンになることです。二重にリンクされたリストはそれを解決しますが、この問題に遭遇した場合、データ構造の再設計は多くの場合オプションではありません。ここの例はやや人為的に制約されていますが、再帰的な代数型のコンテキストで関数型言語で頻繁に現れるパターンを示しています。
ComicSansMS

私のポイントは、この問題に遭遇した場合、それを実装するために使用する言語構成よりも機能設計の不足にあり、それぞれの選択肢に独自のプラスとマイナスがあることです:)
Baldrickk

0

ループは、特定のタスク(主に反復)を達成するための特別な形式の再帰です。いくつかの言語で同じパフォーマンスの再帰スタイルでループを実装できます[1]。また、SICP [2]では、forループが「シンタスティックシュガー」として記述されていることがわかります。ほとんどの命令型プログラミング言語では、forおよびwhileブロックは親関数と同じスコープを使用しています。それにもかかわらず、ほとんどの関数型プログラミング言語には、ループが必要ないため、forループもwhileループも存在しません。

命令型言語にfor / whileループがある理由は、それらを変更することで状態を処理しているためです。しかし、実際には、異なる視点から見た場合、whileブロックを関数自体として考え、パラメーターを取得し、それを処理し、新しい状態を返します(異なるパラメーターで同じ関数を呼び出すこともできます)ループを再帰と考えることができます。

世界は、可変または不変として定義することもできます。世界を一連のルールとして定義し、すべてのルールと現在の状態をパラメーターとして取る究極の関数を呼び出し、同じ機能を持つこれらのパラメーターに従って新しい状態を返します(同じ状態で次の状態を生成します)方法)、それは再帰とループであると言うこともできます。

次の例では、lifeは関数が2つのパラメーター「rules」と「state」を取り、新しい状態は次回のティックで構築されます。

life rules state = life rules new_state
    where new_state = construct_state_in_time rules state

[1]:末尾呼び出しの最適化は、新しい関数を作成する代わりに再帰呼び出しで既存の関数スタックを使用するための関数型プログラミング言語の一般的な最適化です。

[2]:MITのコンピュータープログラムの構造と解釈。https://mitpress.mit.edu/books/structure-and-interpretation-computer-programs


4
@Giorgio私のダウン票ではなく、単なる推測です。ほとんどのプログラマーは、再帰は再帰的な関数呼び出しがあることを意味すると考えていると思います。ループでは、再帰的な関数呼び出しはありません。したがって、この定義に従えば、再帰的な関数呼び出しのないループは再帰の特別な形式であると言うのは明らかに間違っています。
ハイド

1
おそらく、より抽象的な観点から見ると、異なるものと思われるものは、実際には概念的には同じです。人々が何かを学ぶ機会としてとらえるのではなく、期待に応えていないからといって答えを下げると考えるのは、かなり落胆し悲しいことです。「ねえ、見て、これらのものは表面上は違うように見えるが、実際にはより抽象的なレベルでは同じだ」と言おうとするすべての答えは、否決された。
ジョルジオ

3
@Georgio:このサイトの目的は、質問への回答を得ることです。有用で正しい回答は、賛成票に値し、混乱し、役に立たない回答は、反対票に値します。使用される異なる定義を明確にせずに、共通用語の異なる定義を微妙に使用する回答は、混乱を招き、役に立たないものです。答えを知っている場合にのみ意味をなす答えは、いわば役に立たず、作家に用語の優れた理解を示すのに役立つだけです。
ジャックB

2
@JacquesB:「答えを知っている場合にのみ意味をなす答えは、いわば役に立たない...」:これは、読者が既に知っているか、知っていると思うことを確認するだけの答えとも言えます。答えに明確でない用語が導入されている場合、コメントを書いて、投票する前に詳細を尋ねることができます。
ジョルジオ

4
ループは再帰の特別な形式ではありません。計算可能性理論と、理論上のWHILE言語やμ計算などを見てください。はい、一部の言語では、ループを構文シュガーとして使用して、実際に舞台裏で再帰を使用していますが、再帰と反復は同じであるためではなく、同じように表現力があるため、ループを実行できます。
ポリノーム

-1

whileループは再帰とは異なります。

関数が呼び出されると、次のことが行われます。

  1. スタックフレームがスタックに追加されます。

  2. コードポインターが関数の先頭に移動します。

whileループが終了すると、次のことが発生します。

  1. 条件は、何か真かどうかを尋ねます。

  2. その場合、コードはポイントにジャンプします。

一般に、whileループは次の擬似コードに似ています。

 if (x)

 {

      Jump_to(y);

 }

最も重要なのは、再帰とループのアセンブリコード表現とマシンコード表現が異なることです。これは、それらが同じではないことを意味します。同じ結果になる可能性がありますが、異なるマシンコードは、100%同じではないことを証明しています。


2
プロシージャコールとwhileループの実装について話しているが、これらは異なる方法で実装されているため、異なると結論付ける。ただし、概念的には非常に似ています。
ジョルジオ

1
コンパイラに応じて、最適化されたインライン再帰呼び出しは、プレーンループと同じアセンブリを生成する可能性があります。
ハイド

@hyde ...これは、一方が他方を通して表現できるという周知の事実の例にすぎません。それらが同一であることを意味しません。質量とエネルギーに少し似ています。もちろん、同一の出力を生成するすべての方法は「同じ」であると主張できます。世界が有限であれば、すべてのプログラムは最終的にconstexprになります。
ピーター-モニカの復活

@Giorgio Nah、これは論理的な説明であり、実装ではありません。2つのものが同等であることを知っています; ただし、質問(および回答)は結果を得る方法とまったく同じであるため、等価性は同一性ではありません。つまり、アルゴリズム記述(スタックや変数などで表現できる)を必ず含むためです。
ピーター-モニカの復活

1
@ PeterA.Schneiderええ、しかし、この答えは「すべての中で最も重要な...異なるアセンブリコード」と述べていますが、これは正しくありません。
ハイド

-1

繰り返しだけでは再帰とほぼ同等では不十分ですが、スタックでの繰り返しはほぼ同等です。再帰関数は、スタックを使用した反復ループとして再プログラムでき、その逆も可能です。ただし、これは実用的であることを意味するものではなく、特定の状況では、いずれかの形式が他のバージョンよりも明確なメリットをもたらす場合があります。

なぜこれが議論の余地があるのか​​はわかりません。スタックでの再帰と反復は、同じ計算プロセスです。いわば、同じ「現象」です。

私が考えることができる唯一のことは、これらを「プログラミングツール」として見るとき、それらを同じものと考えてはならないことに同意するということです。それらは「数学的に」または「計算的に」同等です(繰り返しではなく、スタックでの繰り返し)実装/問題解決の観点から、いくつかの問題は何らかの形でうまく機能する可能性があり、プログラマーとしてのあなたの仕事はどちらがより適しているかを正しく決定することです。

明確にするために、whileループは本質的に再帰ですか?は明確なnoであるか、少なくとも「スタックがない限り」ではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.