web-dev-qa-db-ja.com

再帰関数呼び出しのreturnステートメントの理由

私は私の心の中で疑いを持っていました。次のサブルーチン(たとえば、リスト内の要素を検索する)の最後にreturnステートメントがあります。

list *search_list(list *l, item_type x) {
  if (l == NULL) return(NULL);
  if (l->item == x)
    return(l);
  else
    return( search_list(l->next, x) );
}

最後にreturnステートメントの意味を取得できません(つまり、return search_list(l-> next、x))。スタックモデルを使用して、この概念をだれかが説明できると本当に助かります。

14
user1369975

Returnステートメントは、現在の関数の呼び出しフレームの即時呼び出し元に値を返します。再帰の場合、この直接の呼び出し元は、同じ関数の別の呼び出しになる可能性があります。

ほとんどの言語では、呼び出した関数の戻り値を(再帰的かどうかにかかわらず)使用しない場合、その戻り値は破棄されるか、診断可能なエラーになります。一部の言語では、最後の関数呼び出しの戻り値が現在の関数呼び出しの戻り値として自動的に再利用されますが、通常の関数呼び出しと再帰的な関数呼び出しを区別しません。

次のようなコードを記述した場合、未使用の戻り値が通知なく破棄されると想定します。

list *search_list(list *l, item_type x) {
  if (l == NULL) return(NULL);
  if (l->item == x)
    return(l);
  else
    search_list(l->next, x); // no return!
}

次にsearch_listは、空のリスト(NULL)の定義済みの値、または最初の項目が検索している値と一致する場合にのみ返します。関数が再帰呼び出しに入るとすぐに、再帰呼び出しの結果が破棄されるため、結果がどうなるかわかりません。

さらに、関数から値を返すことを約束しますが、返す値を指定しないパス(再帰パス)があります。使用する言語に応じて、これは通常、必須の診断または未定義の動作のいずれかになります(これは省略形です:何かが発生する可能性があり、予告なしにいつでも変更される可能性があります。それが台無しになった場合、自分以外の責任を負わないでくださいあなたの最も重要なプレゼンテーション)。欠落している戻り値が機能しているように見えても、次にプログラムを実行したときに(再コンパイルありまたはなしで)変更される場合があります。

2つのこと;探している「x」が見つかった場合にリスト全体を返すことは、必ずしも再帰を使用する必要はありませんが、それはさておき、次のことを考慮してください。

X = "December"の値を探していて、リストがその年の月の数値であり、翌月へのポインターであり、リスト内のl-> itemsが、ヶ月。 (1月、2月、...、12月)。考えられる結果を得るには、3つの収益が必要です。最初に、リストに探しているXが含まれていない場合は、return(NULL)が必要です。 2番目の(return(l))はリストを返します。この場合、「x」が見つかったことを通知します。最後は、スタックモデルの出番です。関数を連続して呼び出すと、次のようにローカル変数(具体的にはl-> item's)が更新されます。

1: l->item = January
   returns search_list(l->next, x)
2: l->item = February
   returns search_list(l->next, x)
3-11: March, April, May, June, July, August, September, October, November
   all return search_list(l->next, x)
12: l->item = December
  This matches the second if() and returns your list, letting you know you found your search item.
1
panhandel