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