課題3: 基本構文の実践

マンダトリー要件

問題1:変数と可変性の理解(20点)

以下のコードについて、コンパイルエラーが発生するか判定し、理由を説明しなさい。

// 1-1
fn main() {
    let x = 5;
    x = 6;
    println!("{}", x);
}

// 1-2
fn main() {
    let mut x = 5;
    x = 6;
    println!("{}", x);
}

// 1-3
fn main() {
    let x = 5;
    let x = x + 1;
    let x = x * 2;
    println!("{}", x);
}

// 1-4
fn main() {
    let mut spaces = "   ";
    spaces = spaces.len();
}

// 1-5
fn main() {
    let spaces = "   ";
    let spaces = spaces.len();
    println!("{}", spaces);
}

各コードについて:

  • コンパイルできるか?(できる/できない)
  • 理由は何か?
  • できる場合、出力は何か?
  • エラーを修正する方法は?(必要な場合)

提出物problem1_analysis.md

問題2:データ型の実装(25点)

以下の関数を実装しなさい。

/// 整数のオーバーフローを安全に処理する
///
/// # Arguments
/// * `a` - 加算される数
/// * `b` - 加算する数
///
/// # Returns
/// * Some(sum) - 成功時
/// * None - オーバーフロー時
fn safe_add(a: u8, b: u8) -> Option<u8> {
    // TODO: 実装
    // ヒント:checked_addメソッドを使用
}

/// タプルの各要素を2倍にする
fn double_tuple(tup: (i32, f64, u8)) -> (i32, f64, u8) {
    // TODO: 実装
}

/// 配列の平均を計算する
fn average(arr: [i32; 5]) -> f64 {
    // TODO: 実装
}

/// 文字が数字かどうか判定
fn is_digit(c: char) -> bool {
    // TODO: 実装
    // ヒント:c >= '0' && c <= '9'
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_safe_add() {
        assert_eq!(safe_add(100, 50), Some(150));
        assert_eq!(safe_add(200, 100), None);
    }

    #[test]
    fn test_double_tuple() {
        assert_eq!(double_tuple((5, 3.0, 2)), (10, 6.0, 4));
    }

    #[test]
    fn test_average() {
        assert_eq!(average([1, 2, 3, 4, 5]), 3.0);
        assert_eq!(average([10, 20, 30, 40, 50]), 30.0);
    }

    #[test]
    fn test_is_digit() {
        assert!(is_digit('5'));
        assert!(!is_digit('a'));
    }
}

提出物problem2.rs、テスト実行結果

問題3:関数の実装(25点)

以下の要件を満たすプログラムを作成しなさい。

要件

  • 摂氏から華氏への変換関数
  • 華氏から摂氏への変換関数
  • ユーザー入力を受け付ける

実装

use std::io;

/// 摂氏を華氏に変換
/// 公式:F = C * 9/5 + 32
fn celsius_to_fahrenheit(celsius: f64) -> f64 {
    // TODO: 実装
}

/// 華氏を摂氏に変換
/// 公式:C = (F - 32) * 5/9
fn fahrenheit_to_celsius(fahrenheit: f64) -> f64 {
    // TODO: 実装
}

fn main() {
    loop {
        println!("温度変換プログラム");
        println!("1. 摂氏 → 華氏");
        println!("2. 華氏 → 摂氏");
        println!("3. 終了");

        let mut choice = String::new();
        io::stdin()
            .read_line(&mut choice)
            .expect("読み込みエラー");

        let choice: u32 = match choice.trim().parse() {
            Ok(num) => num,
            Err(_) => {
                println!("無効な入力です");
                continue;
            }
        };

        match choice {
            1 => {
                // TODO: 摂氏入力を受け付け、華氏に変換
            }
            2 => {
                // TODO: 華氏入力を受け付け、摂氏に変換
            }
            3 => {
                println!("終了します");
                break;
            }
            _ => {
                println!("1-3を選択してください");
            }
        }
    }
}

期待される動作

温度変換プログラム
1. 摂氏 → 華氏
2. 華氏 → 摂氏
3. 終了
> 1
摂氏温度を入力: 0
華氏温度: 32.0°F

> 2
華氏温度を入力: 100
摂氏温度: 37.8°C

> 3
終了します

提出物problem3_temperature.rs

問題4:制御フローの実践(30点)

以下のプログラムを実装しなさい。

4-1: フィボナッチ数列(10点)

/// n番目のフィボナッチ数を計算(反復版)
fn fibonacci_iterative(n: u32) -> u64 {
    // TODO: 実装
    // 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...
}

/// n番目のフィボナッチ数を計算(再帰版)
fn fibonacci_recursive(n: u32) -> u64 {
    // TODO: 実装
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_fibonacci() {
        let expected = vec![0, 1, 1, 2, 3, 5, 8, 13, 21, 34];
        for (i, &expected_val) in expected.iter().enumerate() {
            assert_eq!(fibonacci_iterative(i as u32), expected_val);
            assert_eq!(fibonacci_recursive(i as u32), expected_val);
        }
    }
}

4-2: 素数判定(10点)

/// 素数かどうか判定
fn is_prime(n: u32) -> bool {
    // TODO: 実装
    // ヒント:
    // - 2未満は素数ではない
    // - 2は素数
    // - 偶数は素数ではない(2以外)
    // - √n まで調べれば十分
}

/// n以下の素数をすべて返す
fn primes_up_to(n: u32) -> Vec<u32> {
    // TODO: 実装
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_is_prime() {
        assert!(!is_prime(0));
        assert!(!is_prime(1));
        assert!(is_prime(2));
        assert!(is_prime(3));
        assert!(!is_prime(4));
        assert!(is_prime(5));
        assert!(is_prime(97));
    }

    #[test]
    fn test_primes_up_to() {
        assert_eq!(primes_up_to(10), vec![2, 3, 5, 7]);
        assert_eq!(primes_up_to(20), vec![2, 3, 5, 7, 11, 13, 17, 19]);
    }
}

4-3: 九九表の出力(10点)

/// 九九表を出力する
fn print_multiplication_table() {
    // TODO: 実装
    // 期待される出力:
    //   1  2  3  4  5  6  7  8  9
    //   2  4  6  8 10 12 14 16 18
    //   3  6  9 12 15 18 21 24 27
    //   ...
}

/// 指定された範囲の掛け算表を出力
fn print_custom_table(start: u32, end: u32) {
    // TODO: 実装
    // 例:print_custom_table(5, 7) → 5x5, 6x6, 7x7の表
}

提出物problem4_control_flow.rs

---

ボーナス課題

> ボーナス: 以下はオプションです。マンダトリー要件を完了してから挑戦してください。

ボーナス1:高度な数学関数(10点)

以下の数学関数を実装しなさい。

/// 最大公約数(ユークリッドの互除法)
fn gcd(a: u32, b: u32) -> u32 {
    // TODO: 実装
}

/// 最小公倍数
fn lcm(a: u32, b: u32) -> u32 {
    // TODO: 実装
    // ヒント:lcm(a, b) = a * b / gcd(a, b)
}

/// 階乗
fn factorial(n: u32) -> u64 {
    // TODO: 実装
}

/// 組み合わせ(nCr)
fn combination(n: u32, r: u32) -> u64 {
    // TODO: 実装
    // ヒント:nCr = n! / (r! * (n-r)!)
}

/// べき乗(繰り返し2乗法)
fn power(base: u64, exp: u32) -> u64 {
    // TODO: 実装
    // O(log n)で計算
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_gcd() {
        assert_eq!(gcd(48, 18), 6);
        assert_eq!(gcd(100, 50), 50);
    }

    #[test]
    fn test_combination() {
        assert_eq!(combination(5, 2), 10);
        assert_eq!(combination(10, 3), 120);
    }
}

提出物bonus1_math.rs

ボーナス2:ゲーム実装(10点)

数当てゲームを実装しなさい。

要件

use std::io;
use std::cmp::Ordering;

fn main() {
    println!("数当てゲーム!");
    println!("1から100の数を当ててください");

    let secret_number = 42; // TODO: ランダムな数を生成
                            // ヒント:rand crateを使用

    let mut attempts = 0;

    loop {
        println!("予想を入力してください:");

        let mut guess = String::new();
        io::stdin()
            .read_line(&mut guess)
            .expect("読み込みエラー");

        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => {
                println!("数値を入力してください");
                continue;
            }
        };

        attempts += 1;

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("小さすぎます!"),
            Ordering::Greater => println!("大きすぎます!"),
            Ordering::Equal => {
                println!("正解です!{} 回で当てました!", attempts);
                break;
            }
        }
    }
}

拡張要件

  • ランダムな数の生成(rand crateを使用)
  • 難易度選択(1-10、1-100、1-1000)
  • 試行回数に応じたランク表示
  • リトライ機能

提出物bonus2_game/プロジェクト

ボーナス3:コマンドラインツール(10点)

電卓CLIツールを実装しなさい。

$ cargo run -- 10 + 5
15

$ cargo run -- 20 \* 3
60

$ cargo run -- 100 / 4
25

$ cargo run -- 2 ^ 8
256

要件

  • 基本演算(+、-、*、/)
  • べき乗(^)
  • 括弧のサポート(ボーナス内ボーナス)
  • 適切なエラーハンドリング

提出物bonus3_calculator/プロジェクト

ボーナス4:アルゴリズム実装(10点)

以下のソートアルゴリズムを実装し、性能を比較しなさい。

/// バブルソート
fn bubble_sort(arr: &mut [i32]) {
    // TODO: 実装
}

/// 選択ソート
fn selection_sort(arr: &mut [i32]) {
    // TODO: 実装
}

/// 挿入ソート
fn insertion_sort(arr: &mut [i32]) {
    // TODO: 実装
}

/// クイックソート
fn quick_sort(arr: &mut [i32]) {
    // TODO: 実装
}

/// ベンチマーク実行
fn benchmark_sorts() {
    use std::time::Instant;

    let sizes = vec![100, 1000, 10000];

    for size in sizes {
        let mut data = generate_random_array(size);

        // TODO: 各ソートアルゴリズムの実行時間を測定
    }
}

提出物

  • bonus4_sorting.rs: 実装
  • benchmark_results.md: 性能比較レポート

ボーナス5:パターン実装(10点)

以下の模様を出力するプログラムを作成しなさい。

5-1: ピラミッド

    *
   ***
  *****
 *******
*********

5-2: ダイヤモンド

    *
   ***
  *****
 *******
  *****
   ***
    *

5-3: 数字のピラミッド

    1
   121
  12321
 1234321
123454321

提出物bonus5_patterns.rs

---

評価基準

マンダトリー部分(80点)

項目 配点 評価ポイント
問題1:変数と可変性 20点 概念の正確な理解
問題2:データ型 25点 型システムの活用
問題3:関数実装 25点 実用的なプログラム
問題4:制御フロー 30点 アルゴリズムの実装力

ボーナス部分(20点)

項目 配点 評価ポイント
ボーナス1:数学関数 10点 効率的なアルゴリズム
ボーナス2:ゲーム 10点 ユーザー体験の質
ボーナス3:電卓 10点 パーサーの実装
ボーナス4:ソート 10点 性能分析の深さ
ボーナス5:パターン 10点 ループの制御力

: ボーナスは最大20点まで加算されます。

---

提出方法

ファイル構成

rust-foundations-03/
├── problem1_analysis.md
├── problem2.rs
├── problem3_temperature.rs
├── problem4_control_flow.rs
└── bonus/
    ├── bonus1_math.rs
    ├── bonus2_game/
    ├── bonus3_calculator/
    ├── bonus4_sorting.rs
    ├── bonus5_patterns.rs
    └── benchmark_results.md

テストの実行

各ファイルに対して:

rustc --test problem2.rs
./problem2

# または
cargo test

提出期限

  • マンダトリー:第3章学習後、1週間以内
  • ボーナス:第6章修了時まで
  • ---

    ヒント

    問題1のヒント

  • 不変変数:デフォルトで再代入不可
  • mut:明示的に可変化
  • シャドーイング:同名の変数を再宣言(型も変更可)

問題2のヒント

checked_addの使用例:

let x: u8 = 200;
let y: u8 = 100;
match x.checked_add(y) {
    Some(result) => println!("成功: {}", result),
    None => println!("オーバーフロー"),
}

問題4のヒント

素数判定の最適化:

fn is_prime(n: u32) -> bool {
    if n < 2 { return false; }
    if n == 2 { return true; }
    if n % 2 == 0 { return false; }

    let limit = (n as f64).sqrt() as u32;
    for i in (3..=limit).step_by(2) {
        if n % i == 0 {
            return false;
        }
    }
    true
}

ボーナス2のヒント

rand crateの使用:

[dependencies]
rand = "0.8"

use rand::Rng;

let secret_number = rand::thread_rng().gen_range(1..=100);

---

学習の確認

この課題を通じて、以下を理解できたか確認してください:

  • [ ] 変数の不変性とmut
  • [ ] シャドーイングの仕組み
  • [ ] 基本的なデータ型
  • [ ] 関数の定義と戻り値
  • [ ] 式と文の違い
  • [ ] if、loop、while、forの使い分け
  • [ ] パターンマッチングの基本
  • [ ] エラーハンドリングの初歩

次の章では、Rustの核心である所有権システムを学びます。これまでの知識が基礎となります。