ほとんどの場合、Zeds の単純な再帰に対応しています...
再帰が反復よりも優れていると想定されるのはなぜですか? 特に C++ では。どうしたの...
int myPow (int x, int p) {
int i = 1;
for (int j = 1; j <= p; j++) i *= x;
return i;
}
あなたの答えが間違っている、または悪いと言っているのではありません。再帰的であるために良いと思うという印象を受けただけです。IMO、特に C++ では、そのバイアスにより、プログラムが遅くなったり、プログラムが壊れたりする可能性があります。巨大なスタックを増やしているためにプログラムが遅くなり、キャッシュと仮想メモリのページングが発生します。反復ソリューションが機能する場所でスタック オーバーフローが発生するため、壊れたプログラム。
あなたの答えを見て、それが末尾再帰的であり、とにかく反復に最適化されると考える人もいます。もちろん、それは真実ではありません - 各再帰呼び出しが終了した後、まだ実行すべき乗算があるため、末尾再帰ではありません。問題は、C++ では、末尾再帰の最適化を妨げるより微妙なことがたくさんあるということです。たとえコンパイラがそれらを実行したとしてもです。例えば...
void myrecurse (plan *p)
{
plan i;
i.prev = p;
// more plan setup, checks, and special case handling
myrecurse (&i);
}
この場合、すべての「プラン」インスタンスがスタックに残る必要があります。したがって、スタック フレームを破棄することはできません。したがって、再帰呼び出しの後に実行される操作が正確にゼロであっても、これは反復に最適化できません。plan は POD 構造体であると想定されるため、デストラクタのクリーンアップなどの非表示の操作でさえありません。
ちなみに、これは私が実際のコードで行ったことに基づいています-再帰中に計画されたデータ構造操作ですが、再帰がルート/リーフに到達するまで元のノードでは何も変更されず、必要なすべての新しいノードが正常に実行されました割り当てられ、すべてのロックが取得され、さらに悪化する中断はありません。その時点で、計画インスタンスのリンクされたリストを介して反復が行われ、変更がコミットされます。ロジックは、再帰呼び出しの巻き戻しに関連するフラグメントに分割されるよりも、反復として明確でした。
ここでのポイントは、再帰が自動的に悪いと主張することではありません。デフォルトでは再帰の方が反復よりも優れていると人々が想定しているように見えると、ただ緊張します。