解答

// 著作権 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0

fn fib(n: u32) -> u32 {
    if n < 2 {
        return n;
    } else {
        return fib(n - 1) + fib(n - 2);
    }
}

fn main() {
    let n = 20;
    println!("fib({n}) = {}", fib(n));
}

ここでは、関数から値を返すために return 構文を使っています。講座の後半では、 ブロック内の最後の式が自動的に返されることを学びます。これにより、より簡潔な スタイルのために return キーワードを省略できるようになります。

if の条件 n < 2 には括弧は不要で、これは Rust の標準的な スタイルです。

パニック

この演習では、この関数がいつパニックするかを問います。フィボナッチ数列は 非常に急速に増加します。u32 では、n が 48 に達すると、計算された値は 32 ビット整数の上限 (4,294,967,295) をオーバーフローします。

Rust では、整数演算は デバッグモードcargo run を使うときの デフォルト)でオーバーフローを検査します。オーバーフローが発生すると、 プログラムはパニックし(エラーメッセージを表示してクラッシュし)ます。 リリースモードcargo run --release)では、オーバーフローチェックは デフォルトで無効になっており、値はラップアラウンドし (モジュラー演算)、誤った結果を生成します。

  • 解答をステップごとにたどってください。
  • 再帰呼び出しと、それらがどのように最終結果につながるかを説明してください。
  • 整数オーバーフローの問題について議論してください。u32 では、この関数は n が 47 前後でパニックします。main への入力を変更することで、 これを実演できます。
  • 代替として反復的な解法を示し、その性能とメモリ使用量を再帰的なものと比較してください。 反復的な解法の方がはるかに効率的です。

さらに学ぶ

より発展的な議論として、再帰的なフィボナッチ計算を最適化するためにメモ化や動的 計画法を紹介できますが、これは現在のトピックの範囲を超えています。