課題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;
}
}
}
}
拡張要件:
- ランダムな数の生成(
randcrateを使用) - 難易度選択(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章修了時まで
- 不変変数:デフォルトで再代入不可
- mut:明示的に可変化
- シャドーイング:同名の変数を再宣言(型も変更可)
---
ヒント
問題1のヒント
問題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の核心である所有権システムを学びます。これまでの知識が基礎となります。