課題13: イテレータ (Iterators)

マンダトリー要件

問題1: イテレータの基本(20点)

以下のプログラムを完成させなさい。

fn main() {
    let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    // TODO: イテレータを使って以下を実装
    // 1. 偶数のみをフィルタリング (5点)
    let evens: Vec<i32> = // ここを実装

    // 2. 各要素を2乗 (5点)
    let squared: Vec<i32> = // ここを実装

    // 3. 合計を計算 (5点)
    let sum: i32 = // ここを実装

    // 4. 最初の3つの奇数を取得 (5点)
    let first_three_odds: Vec<i32> = // ここを実装

    println!("Evens: {:?}", evens);
    println!("Squared: {:?}", squared);
    println!("Sum: {}", sum);
    println!("First three odds: {:?}", first_three_odds);
}

期待される出力:

Evens: [2, 4, 6, 8, 10]
Squared: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Sum: 55
First three odds: [1, 3, 5]

---

問題2: カスタムイテレータ - Counter (30点)

以下の仕様に従ってCounterイテレータを実装しなさい。

// 指定された開始値から終了値まで、ステップ値ずつ増加するイテレータ
struct Counter {
    current: i32,
    end: i32,
    step: i32,
}

impl Counter {
    fn new(start: i32, end: i32, step: i32) -> Self {
        // TODO: 実装 (5点)
    }
}

impl Iterator for Counter {
    type Item = i32;

    fn next(&mut self) -> Option<Self::Item> {
        // TODO: 実装 (10点)
    }
}

// ボーナス: size_hintを実装 (5点)
impl Counter {
    fn size_hint(&self) -> (usize, Option<usize>) {
        // TODO: 実装
    }
}

fn main() {
    // テストケース1: 通常のカウント
    let counter = Counter::new(0, 10, 2);
    let result: Vec<i32> = counter.collect();
    assert_eq!(result, vec![0, 2, 4, 6, 8]);

    // テストケース2: 負のステップ
    let counter = Counter::new(10, 0, -2);
    let result: Vec<i32> = counter.collect();
    assert_eq!(result, vec![10, 8, 6, 4, 2]);

    // テストケース3: ステップが大きい場合
    let counter = Counter::new(0, 100, 30);
    let result: Vec<i32> = counter.collect();
    assert_eq!(result, vec![0, 30, 60, 90]);

    println!("All tests passed!");
}

評価基準:

  • newの実装: 5点
  • nextの基本実装: 10点
  • エッジケースの処理(負のステップ等): 10点
  • size_hintの実装: 5点

---

問題3: データ処理パイプライン (30点)

以下のPerson構造体を使って、データ処理パイプラインを実装しなさい。

#[derive(Debug, Clone)]
struct Person {
    name: String,
    age: u32,
    salary: u32,
    department: String,
}

fn main() {
    let people = vec![
        Person { name: "Alice".to_string(), age: 25, salary: 50000, department: "Engineering".to_string() },
        Person { name: "Bob".to_string(), age: 30, salary: 60000, department: "Sales".to_string() },
        Person { name: "Charlie".to_string(), age: 35, salary: 70000, department: "Engineering".to_string() },
        Person { name: "David".to_string(), age: 28, salary: 55000, department: "HR".to_string() },
        Person { name: "Eve".to_string(), age: 32, salary: 65000, department: "Engineering".to_string() },
    ];

    // TODO: 以下の関数を実装

    // 1. Engineering部門の平均給与を計算 (10点)
    let avg_salary = calculate_avg_salary(&people, "Engineering");
    println!("Average Engineering salary: {}", avg_salary);

    // 2. 年齢が30歳以上の人の名前を取得(給与の降順) (10点)
    let senior_members = get_senior_members_sorted(&people);
    println!("Senior members: {:?}", senior_members);

    // 3. 部門ごとの人数をカウント (10点)
    let dept_count = count_by_department(&people);
    println!("Department count: {:?}", dept_count);
}

fn calculate_avg_salary(people: &[Person], dept: &str) -> f64 {
    // TODO: 実装
}

fn get_senior_members_sorted(people: &[Person]) -> Vec<String> {
    // TODO: 実装(年齢30歳以上、給与降順)
}

fn count_by_department(people: &[Person]) -> std::collections::HashMap<String, usize> {
    // TODO: 実装
}

期待される出力:

Average Engineering salary: 61666.67
Senior members: ["Charlie", "Eve", "Bob"]
Department count: {"Engineering": 3, "Sales": 1, "HR": 1}

---

ボーナス課題

ボーナス1: フィボナッチイテレータ (10点)

無限のフィボナッチ数列を生成するイテレータを実装しなさい。

struct Fibonacci {
    curr: u64,
    next: u64,
}

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

impl Iterator for Fibonacci {
    type Item = u64;

    fn next(&mut self) -> Option<Self::Item> {
        // TODO: 実装
    }
}

fn main() {
    let fib = Fibonacci::new();

    // 最初の10個
    let first_10: Vec<u64> = fib.take(10).collect();
    assert_eq!(first_10, vec![0, 1, 1, 2, 3, 5, 8, 13, 21, 34]);

    // 1000以下のフィボナッチ数
    let fib = Fibonacci::new();
    let under_1000: Vec<u64> = fib.take_while(|&x| x < 1000).collect();
    println!("Fibonacci numbers under 1000: {:?}", under_1000);

    // 偶数のフィボナッチ数の最初の5個
    let fib = Fibonacci::new();
    let even_fib: Vec<u64> = fib.filter(|x| x % 2 == 0).take(5).collect();
    println!("First 5 even Fibonacci: {:?}", even_fib);
}

評価基準:

  • 正しいフィボナッチ数列の生成: 5点
  • オーバーフロー処理: 3点
  • コードの簡潔性: 2点

---

ボーナス2: CSV パーサー (10点)

CSVデータをパースするイテレータを実装しなさい。

struct CsvParser<'a> {
    data: &'a str,
    current_line: usize,
}

impl<'a> CsvParser<'a> {
    fn new(data: &'a str) -> Self {
        // TODO: 実装
    }
}

impl<'a> Iterator for CsvParser<'a> {
    type Item = Vec<&'a str>;

    fn next(&mut self) -> Option<Self::Item> {
        // TODO: 実装
        // ヘッダー行はスキップする
    }
}

fn main() {
    let csv_data = "Name,Age,City\n\
                    Alice,25,Tokyo\n\
                    Bob,30,Osaka\n\
                    Charlie,35,Kyoto";

    let parser = CsvParser::new(csv_data);

    for row in parser {
        println!("{:?}", row);
    }

    // 期待される出力:
    // ["Alice", "25", "Tokyo"]
    // ["Bob", "30", "Osaka"]
    // ["Charlie", "35", "Kyoto"]
}

評価基準:

  • 基本的なパース機能: 5点
  • ヘッダースキップ: 2点
  • エラーハンドリング(空行等): 3点

---

ボーナス3: チェーン可能なイテレータアダプタ (10点)

独自のイテレータアダプタを実装しなさい。

// WindowsIterator: 指定されたサイズのスライディングウィンドウを返す
struct Windows<I: Iterator> {
    iter: I,
    window_size: usize,
    buffer: Vec<I::Item>,
}

impl<I: Iterator> Windows<I>
where
    I::Item: Clone,
{
    fn new(iter: I, window_size: usize) -> Self {
        // TODO: 実装
    }
}

impl<I: Iterator> Iterator for Windows<I>
where
    I::Item: Clone,
{
    type Item = Vec<I::Item>;

    fn next(&mut self) -> Option<Self::Item> {
        // TODO: 実装
        // バッファをスライディングウィンドウとして使う
    }
}

// 拡張トレイトを定義
trait IteratorExt: Iterator {
    fn windows(self, size: usize) -> Windows<Self>
    where
        Self: Sized,
        Self::Item: Clone,
    {
        Windows::new(self, size)
    }
}

// すべてのイテレータに実装
impl<I: Iterator> IteratorExt for I {}

fn main() {
    let v = vec![1, 2, 3, 4, 5];

    let windows: Vec<Vec<i32>> = v.iter()
        .copied()
        .windows(3)
        .collect();

    println!("{:?}", windows);
    // [[1, 2, 3], [2, 3, 4], [3, 4, 5]]

    // 応用: 移動平均
    let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
    let moving_avg: Vec<f64> = data.iter()
        .copied()
        .windows(3)
        .map(|w| w.iter().sum::<f64>() / w.len() as f64)
        .collect();

    println!("Moving average: {:?}", moving_avg);
    // [2.0, 3.0, 4.0, 5.0]
}

評価基準:

  • Windowsの基本実装: 5点
  • スライディングウィンドウのロジック: 3点
  • 拡張トレイトの実装: 2点
  • ---

    評価基準

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

    項目 配点 評価ポイント
    問題1:基本操作 20点 イテレータメソッドの正しい使用
    問題2:カスタムイテレータ 30点 Iteratorトレイトの実装
    問題3:データ処理 30点 実践的なパイプライン構築

    ボーナス部分(20点)

    項目 配点 評価ポイント
    ボーナス1:フィボナッチ 10点 無限イテレータの実装
    ボーナス2:CSVパーサー 10点 実用的なイテレータ
    ボーナス3:カスタムアダプタ 10点 高度なイテレータ技術

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

    ---

    提出方法

    ファイル構成

    rust-foundations-13/
    ├── src/
    │   ├── problem1.rs
    │   ├── problem2.rs
    │   ├── problem3.rs
    │   ├── bonus1.rs       # オプション
    │   ├── bonus2.rs       # オプション
    │   └── bonus3.rs       # オプション
    ├── Cargo.toml
    └── README.md
    

    テスト

    cargo test --release
    

    提出期限

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

    ヒント

    問題1のヒント

    // filter, map, sum, take を組み合わせる
    numbers.iter().filter(|&x| x % 2 == 0).copied().collect()
    

    問題2のヒント

    // 負のステップの場合は逆向きに進む
    if self.step > 0 {
        if self.current < self.end {
            // ...
        }
    } else {
        if self.current > self.end {
            // ...
        }
    }
    

    問題3のヒント

    // 平均値の計算
    let sum: u32 = people.iter()
        .filter(|p| p.department == dept)
        .map(|p| p.salary)
        .sum();
    let count = people.iter()
        .filter(|p| p.department == dept)
        .count();
    sum as f64 / count as f64
    

    ---

    学習の確認

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

  • [ ] Iteratorトレイトの仕組み
  • [ ] 主要なイテレータアダプタ(map, filter, fold等)
  • [ ] コンシューマメソッド(collect, sum, count等)
  • [ ] カスタムイテレータの実装方法
  • [ ] イテレータチェーンの構築
  • [ ] ゼロコスト抽象化の意味
  • [ ] 遅延評価の利点

次の章では、クロージャについて学びます。イテレータと組み合わせることで、さらに強力な表現が可能になります。