課題8: 制御構造の実践

マンダトリー要件

問題1:条件分岐の実装(20点)

様々な条件分岐を実装しなさい。

// 数値の分類
fn classify_number(n: i32) -> &'static str {
    // TODO: 実装
    // 正の数: "positive"
    // 負の数: "negative"
    // ゼロ: "zero"
}

// 成績判定
fn grade(score: u32) -> &'static str {
    // TODO: 実装
    // 90-100: "A"
    // 80-89: "B"
    // 70-79: "C"
    // 60-69: "D"
    // 0-59: "F"
}

// 閏年判定
fn is_leap_year(year: u32) -> bool {
    // TODO: 実装
    // 4で割り切れる年は閏年
    // ただし100で割り切れる年は平年
    // ただし400で割り切れる年は閏年
}

// 最大値を返す
fn max_of_three(a: i32, b: i32, c: i32) -> i32 {
    // TODO: 実装
}

// 絶対値
fn abs(n: i32) -> i32 {
    // TODO: 実装(if式を使用)
}

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

    #[test]
    fn test_classify_number() {
        assert_eq!(classify_number(5), "positive");
        assert_eq!(classify_number(-5), "negative");
        assert_eq!(classify_number(0), "zero");
    }

    #[test]
    fn test_grade() {
        assert_eq!(grade(95), "A");
        assert_eq!(grade(85), "B");
        assert_eq!(grade(75), "C");
        assert_eq!(grade(65), "D");
        assert_eq!(grade(55), "F");
    }

    #[test]
    fn test_leap_year() {
        assert!(is_leap_year(2000));
        assert!(is_leap_year(2004));
        assert!(!is_leap_year(1900));
        assert!(!is_leap_year(2001));
    }

    #[test]
    fn test_max_of_three() {
        assert_eq!(max_of_three(1, 2, 3), 3);
        assert_eq!(max_of_three(5, 2, 4), 5);
    }

    #[test]
    fn test_abs() {
        assert_eq!(abs(5), 5);
        assert_eq!(abs(-5), 5);
        assert_eq!(abs(0), 0);
    }
}

提出物problem1_conditionals.rs

問題2:ループの実装(20点)

様々なループ処理を実装しなさい。

// フィボナッチ数列のn番目
fn fibonacci(n: u32) -> u64 {
    // TODO: ループを使って実装
}

// 階乗
fn factorial(n: u32) -> u64 {
    // TODO: ループを使って実装
}

// 1からnまでの合計
fn sum_to_n(n: u32) -> u32 {
    // TODO: forループを使って実装
}

// 配列の最大値
fn find_max(arr: &[i32]) -> Option<i32> {
    // TODO: ループを使って実装
}

// 配列の平均値
fn average(arr: &[f64]) -> Option<f64> {
    // TODO: ループを使って実装
}

// 素数判定
fn is_prime(n: u32) -> bool {
    // TODO: ループを使って実装
}

// nまでの素数を列挙
fn primes_up_to(n: u32) -> Vec<u32> {
    // TODO: 実装
}

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

    #[test]
    fn test_fibonacci() {
        assert_eq!(fibonacci(0), 0);
        assert_eq!(fibonacci(1), 1);
        assert_eq!(fibonacci(10), 55);
    }

    #[test]
    fn test_factorial() {
        assert_eq!(factorial(0), 1);
        assert_eq!(factorial(5), 120);
        assert_eq!(factorial(10), 3628800);
    }

    #[test]
    fn test_sum_to_n() {
        assert_eq!(sum_to_n(10), 55);
        assert_eq!(sum_to_n(100), 5050);
    }

    #[test]
    fn test_find_max() {
        assert_eq!(find_max(&[1, 5, 3, 9, 2]), Some(9));
        assert_eq!(find_max(&[]), None);
    }

    #[test]
    fn test_average() {
        assert_eq!(average(&[1.0, 2.0, 3.0, 4.0, 5.0]), Some(3.0));
        assert_eq!(average(&[]), None);
    }

    #[test]
    fn test_is_prime() {
        assert!(is_prime(2));
        assert!(is_prime(17));
        assert!(!is_prime(4));
        assert!(!is_prime(1));
    }

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

提出物problem2_loops.rs

問題3:match式の実装(20点)

パターンマッチングを活用した処理を実装しなさい。

// 月の日数
fn days_in_month(month: u32, is_leap_year: bool) -> Option<u32> {
    // TODO: matchを使って実装
}

// 演算子による計算
fn calculate(op: char, a: f64, b: f64) -> Option<f64> {
    // TODO: matchを使って実装
    // サポート: +, -, *, /
}

// HTTP ステータスコードの分類
fn classify_status_code(code: u16) -> &'static str {
    // TODO: matchを使って実装
    // 200-299: "Success"
    // 300-399: "Redirection"
    // 400-499: "Client Error"
    // 500-599: "Server Error"
    // その他: "Unknown"
}

// じゃんけんの判定
#[derive(Debug, PartialEq)]
enum Hand {
    Rock,
    Paper,
    Scissors,
}

#[derive(Debug, PartialEq)]
enum Outcome {
    Win,
    Lose,
    Draw,
}

fn play_rps(player: Hand, opponent: Hand) -> Outcome {
    // TODO: matchを使って実装
}

// コインの合計金額(セント)
enum Coin {
    Penny,   // 1セント
    Nickel,  // 5セント
    Dime,    // 10セント
    Quarter, // 25セント
}

fn total_value(coins: &[Coin]) -> u32 {
    // TODO: matchを使って実装
}

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

    #[test]
    fn test_days_in_month() {
        assert_eq!(days_in_month(1, false), Some(31));
        assert_eq!(days_in_month(2, false), Some(28));
        assert_eq!(days_in_month(2, true), Some(29));
        assert_eq!(days_in_month(13, false), None);
    }

    #[test]
    fn test_calculate() {
        assert_eq!(calculate('+', 5.0, 3.0), Some(8.0));
        assert_eq!(calculate('-', 5.0, 3.0), Some(2.0));
        assert_eq!(calculate('*', 5.0, 3.0), Some(15.0));
        assert_eq!(calculate('/', 6.0, 3.0), Some(2.0));
        assert_eq!(calculate('/', 5.0, 0.0), None);
    }

    #[test]
    fn test_classify_status_code() {
        assert_eq!(classify_status_code(200), "Success");
        assert_eq!(classify_status_code(404), "Client Error");
        assert_eq!(classify_status_code(500), "Server Error");
    }

    #[test]
    fn test_rps() {
        assert_eq!(play_rps(Hand::Rock, Hand::Scissors), Outcome::Win);
        assert_eq!(play_rps(Hand::Rock, Hand::Paper), Outcome::Lose);
        assert_eq!(play_rps(Hand::Rock, Hand::Rock), Outcome::Draw);
    }
}

提出物problem3_pattern_matching.rs

問題4:実践的な制御フロー(20点)

複雑な制御フローを実装しなさい。

// FizzBuzz
fn fizzbuzz(n: u32) -> Vec<String> {
    // TODO: 実装
    // 3の倍数: "Fizz"
    // 5の倍数: "Buzz"
    // 15の倍数: "FizzBuzz"
    // その他: 数値の文字列
}

// 数値推測ゲーム(1-100)
struct GuessGame {
    secret: u32,
    attempts: u32,
}

impl GuessGame {
    fn new(secret: u32) -> Self {
        // TODO: 実装
    }

    fn guess(&mut self, guess: u32) -> GuessResult {
        // TODO: 実装
    }
}

#[derive(Debug, PartialEq)]
enum GuessResult {
    Correct,
    TooHigh,
    TooLow,
}

// コマンドパーサー
enum Command {
    Move { x: i32, y: i32 },
    Draw { color: String },
    Clear,
    Exit,
}

fn parse_command(input: &str) -> Option<Command> {
    // TODO: 実装
    // "move 10 20" -> Move { x: 10, y: 20 }
    // "draw red" -> Draw { color: "red" }
    // "clear" -> Clear
    // "exit" -> Exit
}

// ローマ数字変換(1-20)
fn to_roman(n: u32) -> Option<String> {
    // TODO: matchを使って実装
}

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

    #[test]
    fn test_fizzbuzz() {
        let result = fizzbuzz(15);
        assert_eq!(result[0], "1");
        assert_eq!(result[2], "Fizz");
        assert_eq!(result[4], "Buzz");
        assert_eq!(result[14], "FizzBuzz");
    }

    #[test]
    fn test_guess_game() {
        let mut game = GuessGame::new(50);
        assert_eq!(game.guess(25), GuessResult::TooLow);
        assert_eq!(game.guess(75), GuessResult::TooHigh);
        assert_eq!(game.guess(50), GuessResult::Correct);
    }

    #[test]
    fn test_to_roman() {
        assert_eq!(to_roman(1), Some("I".to_string()));
        assert_eq!(to_roman(4), Some("IV".to_string()));
        assert_eq!(to_roman(9), Some("IX".to_string()));
        assert_eq!(to_roman(21), None);
    }
}

提出物problem4_control_flow.rs

---

ボーナス課題

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

ボーナス1:計算機(5点)

簡易計算機を実装しなさい。

enum Token {
    Number(f64),
    Plus,
    Minus,
    Multiply,
    Divide,
}

fn tokenize(input: &str) -> Vec<Token> {
    // TODO: 実装
}

fn evaluate(tokens: Vec<Token>) -> Result<f64, &'static str> {
    // TODO: 実装
    // 簡易版:左から順に計算
}

fn main() {
    let input = "10 + 5 * 2";
    let tokens = tokenize(input);
    match evaluate(tokens) {
        Ok(result) => println!("結果: {}", result),
        Err(e) => println!("エラー: {}", e),
    }
}

提出物bonus1_calculator/プロジェクト

ボーナス2:ステートマシン(5点)

信号機のステートマシンを実装しなさい。

#[derive(Debug, PartialEq)]
enum TrafficLight {
    Red,
    Yellow,
    Green,
}

impl TrafficLight {
    fn new() -> Self {
        // TODO: 実装
    }

    fn next(&mut self) {
        // TODO: 実装
    }

    fn duration(&self) -> u32 {
        // TODO: 実装
        // Red: 60秒, Yellow: 5秒, Green: 55秒
    }
}

fn main() {
    let mut light = TrafficLight::new();

    for _ in 0..10 {
        println!("{:?}: {}秒", light, light.duration());
        light.next();
    }
}

提出物bonus2_state_machine/プロジェクト

ボーナス3:マインスイーパー(簡易版)(5点)

マインスイーパーのロジックを実装しなさい。

struct Cell {
    is_mine: bool,
    is_revealed: bool,
    adjacent_mines: u8,
}

struct Board {
    width: usize,
    height: usize,
    cells: Vec<Vec<Cell>>,
}

impl Board {
    fn new(width: usize, height: usize) -> Self {
        // TODO: 実装
    }

    fn place_mines(&mut self, positions: Vec<(usize, usize)>) {
        // TODO: 実装
    }

    fn calculate_adjacent(&mut self) {
        // TODO: 実装
    }

    fn reveal(&mut self, x: usize, y: usize) -> RevealResult {
        // TODO: 実装
    }
}

enum RevealResult {
    Mine,
    Safe(u8),
}

提出物bonus3_minesweeper/プロジェクト

ボーナス4:テキストアドベンチャー(5点)

シンプルなテキストアドベンチャーゲームを実装しなさい。

enum Room {
    Entrance,
    Hallway,
    Treasure,
    Trap,
}

enum Action {
    North,
    South,
    East,
    West,
    Take,
}

struct Game {
    current_room: Room,
    has_key: bool,
    is_alive: bool,
}

impl Game {
    fn new() -> Self {
        // TODO: 実装
    }

    fn perform_action(&mut self, action: Action) -> String {
        // TODO: 実装
        // 部屋の移動、アイテム取得など
    }

    fn is_game_over(&self) -> bool {
        // TODO: 実装
    }
}

提出物bonus4_text_adventure/プロジェクト

ボーナス5:正規表現マッチャー(簡易版)(5点)

簡易的な正規表現マッチャーを実装しなさい。

// サポート:
// . : 任意の1文字
// * : 直前のパターンの0回以上の繰り返し
// ^ : 行頭
// $ : 行末

fn regex_match(pattern: &str, text: &str) -> bool {
    // TODO: 実装
}

fn main() {
    assert!(regex_match("a.c", "abc"));
    assert!(regex_match("a*b", "aaab"));
    assert!(regex_match("^hello", "hello world"));
    assert!(regex_match("world$", "hello world"));
}

提出物bonus5_regex_matcher/プロジェクト

---

評価基準

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

項目 配点 評価ポイント
問題1:条件分岐 20点 if式の適切な使用
問題2:ループ 20点 ループの使い分け
問題3:パターンマッチ 20点 matchの活用
問題4:制御フロー 20点 複雑な処理の実装

ボーナス部分(20点)

項目 配点 評価ポイント
ボーナス1:計算機 5点 トークン処理
ボーナス2:ステートマシン 5点 状態管理
ボーナス3:マインスイーパー 5点 ゲームロジック
ボーナス4:アドベンチャー 5点 インタラクション
ボーナス5:正規表現 5点 パターンマッチング

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

---

提出方法

ファイル構成

rust-foundations-08/
├── problem1_conditionals.rs
├── problem2_loops.rs
├── problem3_pattern_matching.rs
├── problem4_control_flow.rs
└── bonus/
    ├── bonus1_calculator/
    ├── bonus2_state_machine/
    ├── bonus3_minesweeper/
    ├── bonus4_text_adventure/
    └── bonus5_regex_matcher/

提出期限

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

---

ヒント

問題1のヒント

is_leap_year実装:

fn is_leap_year(year: u32) -> bool {
    (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
}

問題2のヒント

fibonacci実装:

fn fibonacci(n: u32) -> u64 {
    if n == 0 { return 0; }
    if n == 1 { return 1; }

    let mut a = 0;
    let mut b = 1;

    for _ in 2..=n {
        let temp = a + b;
        a = b;
        b = temp;
    }

    b
}

問題3のヒント

days_in_month実装:

fn days_in_month(month: u32, is_leap_year: bool) -> Option<u32> {
    match month {
        1 | 3 | 5 | 7 | 8 | 10 | 12 => Some(31),
        4 | 6 | 9 | 11 => Some(30),
        2 if is_leap_year => Some(29),
        2 => Some(28),
        _ => None,
    }
}

---

学習の確認

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

  • [ ] if式の使い方と値の返却
  • [ ] loop、while、forの使い分け
  • [ ] matchによるパターンマッチング
  • [ ] ガード条件の使用
  • [ ] if letとwhile letの活用
  • [ ] ループラベルとbreak
  • [ ] 列挙型とmatchの組み合わせ

次の章では、関数とクロージャを学びます。高階関数やイテレータとの組み合わせについて理解を深めます。