解答
// 著作権 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への入力を変更することで、 これを実演できます。 - 代替として反復的な解法を示し、その性能とメモリ使用量を再帰的なものと比較してください。 反復的な解法の方がはるかに効率的です。
さらに学ぶ
より発展的な議論として、再帰的なフィボナッチ計算を最適化するためにメモ化や動的 計画法を紹介できますが、これは現在のトピックの範囲を超えています。