演習: ループを使用してデータを反復処理する

完了

この演習では、ループを使用して自動車の注文を反復処理するように自動車工場プログラムを変更します。

main 関数を更新して、注文の完全なセットを処理するループ式を追加します。 ループ構造は、コード内の冗長性を減らすのに役立ちます。 コードを簡略化することで、注文金額を簡単に増やすことができます。

car_factory 関数では、別のループを追加して、範囲外の値での実行時のパニックを回避します。

課題は、コンパイルして実できるようにサンプル コードを完成させることです。

この演習用のサンプル コードを操作するには、次の 2 つの方法があります。

  • コードをコピーし、ローカルの開発環境で編集します。
  • 準備済みの Rust Playground 内でコードを開きます。

注意

サンプル コードで、todo! マクロを探します。 このマクロは、完了または更新する必要があるコードを示しています。

プログラムを読み込む

前回の演習でプログラム コードを閉じた場合は、この準備済みの Rust Playground 内でコードを再び開くことができます。

プログラムをリビルドし、コンパイラ エラーなしで実行されることを確認してください。

ループ式を使用してアクションを繰り返す

さらに多くの注文をサポートするには、プログラムを更新する必要があります。 現在のコード構造では、冗長なステートメントを使用して 6 つの注文をサポートしています。 冗長性は扱いにくく、維持するのが困難です。

ループ式を使用して各注文を作成するアクションを繰り返すことで、構造を簡略化できます。 簡略化されたコードを使用して、多数の注文をすばやく作成できます。

  1. main 関数で、次のステートメントを削除します。 このコード ブロックでは、order 変数を定義して設定し、自動車の注文に対して car_factory 関数と println! マクロを呼び出し、各注文を orders ハッシュ マップに挿入します。

        // Order 6 cars
        // - Increment "order" after each request
        // - Add each order <K, V> pair to "orders" hash map
        // - Call println! to show order details from the hash map
    
        // Initialize order variable
        let mut order = 1;
    
        // Car order #1: Used, Hard top
        car = car_factory(order, 1000);
        orders.insert(order, car);
        println!("Car order {}: {:?}", order, orders.get(&order));
    
        ...
    
        // Car order #6: Used, Hard top
        order = order + 1;
        car = car_factory(order, 4000);
        orders.insert(order, car);
        println!("Car order {}: {:?}", order, orders.get(&order));
    
  2. 削除されたステートメントを次のコード ブロックに置き換えます。

        // Start with zero miles
        let mut miles = 0;
    
        todo!("Add a loop expression to fulfill orders for 6 cars, initialize `order` variable to 1") {
    
            // Call car_factory to fulfill order
            // Add order <K, V> pair to "orders" hash map
            // Call println! to show order details from the hash map        
            car = car_factory(order, miles);
            orders.insert(order, car);
            println!("Car order {}: {:?}", order, orders.get(&order));
    
            // Reset miles for order variety
            if miles == 2100 {
                miles = 0;
            } else {
                miles = miles + 700;
            }
        }
    
  3. 6 台の自動車の注文を作成するアクションを繰り返すループ式を追加します。 1 に初期化された order 変数が必要です。

  4. プログラムをビルドします。 コードがエラーなしでコンパイルされることを確認します。

次の例のような出力が表示されます。

Car order 1: Some(Car { color: "Blue", motor: Manual, roof: true, age: ("New", 0) })
Car order 2: Some(Car { color: "Green", motor: SemiAuto, roof: false, age: ("Used", 700) })
Car order 3: Some(Car { color: "Red", motor: Automatic, roof: true, age: ("Used", 1400) })
Car order 4: Some(Car { color: "Silver", motor: SemiAuto, roof: false, age: ("Used", 2100) })
Car order 5: Some(Car { color: "Blue", motor: Manual, roof: true, age: ("New", 0) })
Car order 6: Some(Car { color: "Green", motor: Automatic, roof: true, age: ("Used", 700) })

自動車の注文を 11 に増やす

これで、プログラムではループを使用して、6 台の自動車の注文を満たすようになりました。 6 台を超える自動車を注文するとどうなりますか?

  1. 11 台の自動車を注文するには、main 関数でループ式を次のように更新します。

        todo!("Update the loop expression to create 11 cars");
    
  2. プログラムトをリビルドします。 実行時に、プログラムはパニックになります。

    Compiling playground v0.0.1 (/playground)
        Finished dev [unoptimized + debuginfo] target(s) in 1.26s
        Running `target/debug/playground`
    thread 'main' panicked at 'index out of bounds: the len is 4 but the index is 4', src/main.rs:34:29
    

この問題を解決する方法を見てみましょう。

ループ式を使用して実行時のパニックを防ぐ

car_factory 関数では、if または else 式を使用して、colors 配列の color インデックスの値を確認します。

    // Prevent panic: Check color index for colors array, reset as needed
    // Valid color = 1, 2, 3, or 4
    // If color > 4, reduce color to valid index
    let mut color = order as usize;
    if color > 4 {        
        // color = 5 --> index 1, 6 --> 2, 7 --> 3, 8 --> 4
        color = color - 4;
    }

colors 配列には 4 つの要素があり、有効な color インデックスの範囲は 0 から 3 です。 条件式では、color インデックスが 4 より大きいかどうかを確認します (color インデックスが 4 に等しいかどうかは確認しません。後でこの関数で、配列にインデックスを付けて自動車の色を割り当てるときに、インデックス値から 1 を減算します (color - 1)。color 値 4 は配列への colors[3] として処理されます)。

現在の if または else 式は、8 台以下の自動車を注文するときに、実行時のパニックを防ぐのに適しています。 しかし、11 台の自動車を注文する場合、プログラムは 9 台目の注文でパニックになります。 より堅牢になるように式を調整する必要があります。 このように改善するために、別のループ式を使用します。

  1. car_factory 関数で、if/else 条件ステートメントをループ式に置き換えます。 color インデックス値が 4 より大きい場合の実行時のパニックを防ぐために、次の擬似コード ステートメントを修正します。

        // Prevent panic: Check color index, reset as needed
        // If color = 1, 2, 3, or 4 - no change needed
        // If color > 4, reduce to color to a valid index
        let mut color = order as usize;
        todo!("Replace `if/else` condition with a loop to prevent run-time panic for color > 4");
    

    ヒント

    この場合、if/else 条件からループ式への変更は、実際には非常にシンプルです。

  2. プログラムをビルドします。 コードがエラーなしでコンパイルされることを確認します。

次の出力が表示されます。

Car order 1: Some(Car { color: "Blue", motor: Manual, roof: true, age: ("New", 0) })
Car order 2: Some(Car { color: "Green", motor: SemiAuto, roof: false, age: ("Used", 700) })
Car order 3: Some(Car { color: "Red", motor: Automatic, roof: true, age: ("Used", 1400) })
Car order 4: Some(Car { color: "Silver", motor: SemiAuto, roof: false, age: ("Used", 2100) })
Car order 5: Some(Car { color: "Blue", motor: Manual, roof: true, age: ("New", 0) })
Car order 6: Some(Car { color: "Green", motor: Automatic, roof: true, age: ("Used", 700) })
Car order 7: Some(Car { color: "Red", motor: Manual, roof: true, age: ("Used", 1400) })
Car order 8: Some(Car { color: "Silver", motor: SemiAuto, roof: false, age: ("Used", 2100) })
Car order 9: Some(Car { color: "Blue", motor: Automatic, roof: true, age: ("New", 0) })
Car order 10: Some(Car { color: "Green", motor: SemiAuto, roof: false, age: ("Used", 700) })
Car order 11: Some(Car { color: "Red", motor: Manual, roof: true, age: ("Used", 1400) })

解決策

この Rust Playground 内で、プログラム出力をこの演習のソリューションと比較できます。