再帰は、プログラミング時に「あまりにも賢い」ことのインスタンスですか?


8

私はいくつかの本を読んだ経験から学んだことですが、コードを最適化できないほどに最適化したり、チームで作業したり、作業しているときでも、非常に高速で非常に複雑な問題の解決策を思いついたりすることは望ましくありません。あなた自身で、しばらくしてあなたの賢い解決策を理解する必要があります。

私の質問は、再帰は同じように扱われるべきですか?平均的なプログラマーは再帰を簡単に理解できるので、再帰を免責して使用すべきですか、それとも平均的なプログラマーは再帰をあまりよく理解せず、チーム全体の生産性のために再帰から離れるべきですか?

「再帰を理解していないプログラマーは一粒の価値がないので、心配しないでください」という簡単な答えがあることは知っていますが、皆さんが実際に体験したい経験があるかどうか疑問に思っていました私が述べたばかりの意見よりも問題を明らかにするであろう共有。


2
この質問は、programmers.stackexchange.com / questions / 24997 /…とよく似ています。良い答えがいくつかあります。
Nicole

4
再帰を理解していない場合、どうすれば平均的なプログラマになれますか?(「任意のプログラマ」との違いに注意してください)

回答:


22

平均的なプログラマーは再帰を簡単に理解できるので、再帰を免責して使用すべきですか、それとも平均的なプログラマーは再帰をあまりよく理解せず、チーム全体の生産性のために再帰から離れるべきですか?

平均的なプログラマーは再帰を完全に理解していると思います。実際、プログラマーがコンピューターサイエンスまたはソフトウェアエンジニアリングの学位を取得している場合は、ほとんど保証されています。確かに、世の中には平均以下のプログラマーが何人かいますが、チームに彼らを望まないでしょう。

この場合、平均的なプログラマーと優れたプログラマーの違いは、再帰を使用する場合と使用しない場合を区別しています。そして、それは解決される問題とそれを解決するために使用される言語に依存します。

  • 関数型プログラミング言語を使用している場合、再帰はさまざまな問題に対する自然で効率的なソリューションです。(テール再帰最適化ルール!)

  • OOまたはプレーンな手続き型言語を使用している場合、再帰は効率が悪くなる可能性があり、スタックオーバーフローが原因で問題が発生する可能性があります。したがって、場合によっては、再帰的なソリューションではなく反復的なソリューションを選択します。ただし、他の場合では、再帰的ソリューションは非常にシンプルでエレガントなので、(おそらくより効率的な)反復ソリューションは「賢すぎる」ソリューションになります。(たとえば、問題がツリー/グラフのバックトラック、構築またはウォーキングなどを必要とする場合、再帰は多くの場合より単純です。)


1
+1末尾再帰コメント。多くの場合、再帰を削除しようとすると、コードに多数のスタック構造とループができ、コールスタックコンポーネントが効果的に複製されます。
2010

1
@Orbling-それは本当ですが、配列またはarraylistを使用して実装されたスタックは、スレッドの呼び出しスタックよりスケーラブルである可能性があります。特に、スレッド呼び出しスタックは、多くの言語実装では拡張できないためです。
スティーブンC

ええ、それは再帰的な関数設計の永遠の問題です。理想的には、プログラマーよりも言語デザイナーが取り組むべき問題のほうが多いです。
2013年

確かに、世の中には平均以下のプログラマーが何人かいます」:わかりました、言わなければなりません...非常に定義すると、世の中のプログラマーの半分は平均を下回り、その半分は平均を「非常に下回る」です。
Lawrence Dol 2015

@LawrenceDol-「非常に」が何かの半分を意味すると主張する人は見たことがない 定義のソースは何ですか?(ペダントリーは事実に基づいている場合にのみ価値があるため、これも尋ねる必要があります。)
スティーブンC

42

一部の問題は自然に再帰的です。これらのケースで反復的なソリューションを考え出すことは、実際には再帰的なソリューションよりも不格好で複雑な場合があります。良い例は、階層ツリー構造をトラバースする必要のあるアルゴリズムです。これは、プログラミングでは珍しいことではありません。

TL; DRバージョン:いいえ。


9
同意した。物事を「有害」としてラベル付けし、極端にそれらを回避することはより危険です-これは常に混乱を招き、前進を続けるのが困難になるでしょう。
heretik

11

再帰は、ほとんどの関数型プログラミング言語の基本原則です。関数型言語の反復(ループ)は、通常、再帰によって行われます。

関数型言語は、より多くのプロセッサコアをエレガントに処理する必要があるため、最近、ややルネッサンスになっています。関数型言語は、可変構造のロックに伴う複雑さを伴うことなく、プログラムについてより適切に推論する方法を提供することにより、この種の並行性を実現するのに役立ちます。


+1良いコメント。「ルネッサンス」が完全に正しいのか、マルチコアの理由はわかりませんが。彼らは常に彼らについて知っている人々のお気に入りのパラダイムでしたが、主流への彼らの増殖は決して素晴らしいことではありませんでした。関数型言語はますます主流になり、その問題を解決するためにスレッド全体が必要になります。(programmers.stackexchange.com/questions/20036/…すでにお読みになっています)
Orbling

11

ツリー構造のウォーク(たとえば、特定のBツリーノードの検索ではなくディレクトリ構造全体のウォーク)などの問題は、再帰の使用に最適です。非再帰的同等物は、多くの場合、独自のスタックの管理をさらに複雑にします。

このような場合、再帰が最善かつ最も単純なソリューションであり、理解が最も簡単です。


7

適切な場合は再帰を使用すると言いますが、明示的な再帰を回避する方法を常に模索しています。

たとえば、ツリー構造を手動でトラバースする必要がある場合は、はい、再帰を使用する必要があります。誰かが再帰的なツリートラバーサルを理解できないほど愚かである場合、反復バージョンの頭も尾も作成されません。

しかし、より適切なオプションは、「ツリー内のすべてに対してこの操作を実行する」ということがわかるまで、再帰を抽象化するイテレータを使用することです。


1
真(および+1)ですが、もちろん、誰かが抽象化を記述しなければなりません。
ローレンスドル

1
@Software Monkeyが言うように、まだ通常は再帰的にコーディングする必要がありますが、「使用」のためのイテレータへの抽象化は問題ありません。
2010

1
+1は、再帰バージョンを理解しないだろう誰かが反復バージョンを理解しない可能性が高いという点です。残念ながら、その人はまた、反復バージョンを理解していないことに気付かないでしょう。
Larry Coleman、

3

再帰は、コードのクリーン度の観点から最も単純なメカニズムです。速度が絶対に重要な場合は、問題のパラメータの2D配列を使用できます。ドータープロセスの生成は、単に配列に別のアイテムを追加することです。私はかつてアセンブラの三重対角ソルバーを行いましたが、それは当時は大変なことでした。インスタンスごとに必要なコンテキストはレベルごとに8ワードであり、各サブ問題は前の問題の3分の1でした。この「ライブラリー」は、他のすべての実装の問題を打ち負かしたため、人気がありました。しかし、それはプログラミングではかなりまれな状況なので、このソリューションを利用できるからといって、「時期尚早の最適化」に屈する必要はありません。明らかに、いくつかのことについては、再帰101の例のような「階乗の計算」のように、それはひどい過剰です。しかし、ほとんどのアプリでは、

アプリに使用する簡単なスペルチェッカーがあります(マイナーなスペルミスの修正に関するヒントを提供したい)。2つの文字列間の「距離」を計算し、削除と追加を許可します。これにより、ツリー構造が大きくなる可能性があり、近い一致のみを対象とするため、ブランチがトリミングされます。再帰では、おそらく20行のコードになります(私はFortranとCの両方のバージョンを持っています)。それ以外の場合は厄介だと思います。そのツリーについて考えるよりも、プログラミング/デバッグ検証の方がはるかに簡単でした!


0

私は理解していますが、明示的に使用したことはありません。自分自身をプログラマーと呼ぶ人は、スタックとヒープが何であるかを知る必要があるのと同じくらい、それが何であるかを知っているか学ぶべきだと思います。ねえ、誰もが気まぐれな再帰を介して三目並べで最小/最大がどのように機能するかを知る必要があります。

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