# Question17 - スタックトレースの読解 本資料の練習問題は、複数のプログラミング言語(Java / C# / PHP / Python)で共通して利用できるよう記述されています。静的型付け・動的型付けの違いや文字列の連結ルールなど、言語固有の文法仕様に基づき、問題文の表現をご使用の言語の仕様に適宜読み替えて進めてください。 ## この問題のねらい - 例外が呼び出しの階層をさかのぼって伝播していく様子を、その経路を示す情報(スタックトレース)から読み取り、発生元と原因を特定できるようにする - 例外の発生元から離れた呼び出し元でも、伝播してきた例外を捕捉できることを理解する 以下の設問はそれぞれ独立したプログラムとして実装してください。 --- ## 設問1 次の3段階の呼び出し構造を持つ処理を実装してください(この3つの処理は、以降の要件に沿って修正する際にも変更しないものとします)。 1. 平均点レポート処理(最上位):整数の一覧(配列)を受け取り、2の処理を呼び出して平均点を求めたうえで、「平均点は(平均点)点です」という文字列を出力する。 2. 平均点算出処理(中間):整数の一覧を受け取り、一覧に含まれる値の合計を求めたうえで、3の処理に「合計」と「一覧の要素数」を渡して呼び出し、その結果を返す。 3. 割り算処理(最下位):2つの数値(割られる数・割る数)を受け取り、割り算した結果を返すだけの処理。 上記1〜3のいずれの処理内でも、エラー(例外的な状況)を捕捉する記述は行わないでください。 ### 手順・要件 1. まず、次の処理を行う呼び出し元プログラムを作成してください。 - 得点の一覧 `[80, 90, 70]` を渡して1の処理を呼び出す - 続けて、空の得点の一覧 `[]` を渡して1の処理を呼び出す 2. この状態で実行すると、1回目の呼び出しは正常に完了しますが、2回目の呼び出しでは「合計÷要素数」が「合計÷0」という割り算になり、実行時にエラー(例外的な状況)が発生します。1〜3のどの処理でもこのエラーを捕捉していないため、プログラムはエラーの発生元から順に、そこに至るまでの3→2→1の呼び出し経路をコンソール(標準エラー出力)に表示しながら異常終了します。これを**スタックトレース**と呼びます。 3. 実際にこの状態で一度実行し、表示されたスタックトレースから「1〜3のうちどの処理の中で」「何が原因で」エラーが発生したのかを確認してください。 4. 確認した内容をもとに、呼び出し元プログラムを次のとおり修正してください(1〜3の処理内容自体は変更しないこと)。 - 得点の一覧の要素数が0であることが原因でエラーが発生した場合、プログラムを異常終了させず、代わりに「採点データがないため、平均点を計算できませんでした」と出力すること - 得点の一覧に要素がある場合は、これまでどおり平均点を出力すること ### 実行例 - 得点の一覧が `[80, 90, 70]` の場合 - 出力: `平均点は80点です` - 得点の一覧が `[]`(空)の場合 - 出力: `採点データがないため、平均点を計算できませんでした` --- ## 設問2 商品の情報として、名前(文字列)と価格(整数)を保持するデータの入れ物(以下「Product」とする)を作成してください。名前・価格は完全非公開にし、それぞれ値を取得する手段(アクセサ)を用意してください。 次の2段階の呼び出し構造を持つ処理を実装してください(この2つの処理は、以降の要件に沿って修正する際にも変更しないものとします)。 1. 商品検索処理:複数のProductからなる一覧(在庫一覧)と、探したい商品名を受け取り、一覧の中から名前が一致するProductを探して返す。一致するものがなければ、「値が存在しないことを示す特別な値」を返す。 2. 検索結果表示処理:在庫一覧と探したい商品名を受け取り、1の処理を呼び出して検索結果を取得したうえで、その検索結果に対して価格を取得する手段(アクセサ)を呼び出し、「(商品名)の価格は(価格)円です」という文字列を出力する。検索結果が「値が存在しないことを示す特別な値」かどうかは確認せず、そのままアクセサを呼び出す処理として実装してください。 上記1・2のいずれの処理内でも、エラー(例外的な状況)を捕捉する記述は行わないでください。 ### 手順・要件 1. まず、次の処理を行う呼び出し元プログラムを作成してください。 - 名前`りんご`・価格`150`、名前`みかん`・価格`100`の2つのProductからなる在庫一覧を作成する - 商品名`りんご`を指定して2の処理を呼び出す - 続けて、在庫一覧に存在しない商品名`バナナ`を指定して2の処理を呼び出す 2. この状態で実行すると、1回目の呼び出しは正常に完了しますが、2回目の呼び出しでは、1の処理が「値が存在しないことを示す特別な値」を返し、2の処理がその値に対してそのままアクセサを呼び出そうとするため、実行時にエラー(例外的な状況)が発生します。1・2のどちらの処理でもこのエラーを捕捉していないため、プログラムはエラーの発生元(2の処理の中で、アクセサを呼び出している箇所)から呼び出し元までの経路を示すスタックトレースを表示しながら異常終了します。 3. 実際にこの状態で一度実行し、表示されたスタックトレースから、2つの処理のうち「どちらの処理の中で」エラーが発生しているかを確認してください(設問1と異なり、今回は3段階ではなく1・2の2段階の呼び出しになっている点にも注目してください)。 4. 確認した内容をもとに、**2. 検索結果表示処理**を次のとおり修正してください(1. 商品検索処理・呼び出し元プログラムは変更しないこと)。 - 検索結果が「値が存在しないことを示す特別な値」だった場合、価格を取得する処理を呼び出さずに、代わりに「(商品名)は見つかりませんでした」と出力すること - 検索結果が見つかった場合は、これまでどおり価格を出力すること ### 実行例 - `りんご`を検索した場合 - 出力: `りんごの価格は150円です` - `バナナ`を検索した場合 - 出力: `バナナは見つかりませんでした` --- ## 設問3 会員登録の入力値チェックを行う、次の3段階の呼び出し構造を持つ処理を実装してください(この3つの処理は、以降の要件に沿って修正する際にも変更しないものとします)。 1. 会員登録処理(最上位):名前(文字列)と年齢(整数)を受け取り、2の処理を呼び出して入力チェックを行ったうえで、チェックを通過した場合は「(名前)を登録しました」という文字列を出力する。 2. 入力チェック処理(中間):名前と年齢を受け取り、まず3の処理に名前を渡して呼び出す。3の処理が正常に終了した後、年齢が0未満だった場合、エラー(例外的な状況)を意図的に発生させる。 3. 名前チェック処理(最下位):名前を受け取り、値が空文字列だった場合、エラー(例外的な状況)を意図的に発生させる。 - 2の処理が発生させるエラーと、3の処理が発生させるエラーは、呼び出し元でどちらが原因かを区別できるよう、それぞれ異なる種類として定義してください。 - 上記1〜3のいずれの処理内でも、発生させたエラーを捕捉する記述は行わないでください。 ### 手順・要件 1. まず、次の処理を行う呼び出し元プログラムを作成してください。 - 名前`花子`・年齢`20`を指定して1の処理を呼び出す - 名前を空文字列・年齢`20`を指定して1の処理を呼び出す - 名前`太郎`・年齢`-5`を指定して1の処理を呼び出す 2. この状態で実行すると、1つ目の呼び出しは正常に完了しますが、2つ目の呼び出しでは3の処理の中で、3つ目の呼び出しでは2の処理の中で、それぞれエラーが発生し、プログラムはその都度スタックトレースを表示しながら異常終了します。 3. 実際に2つ目・3つ目の呼び出しの状態で実行し、表示されるスタックトレースの内容(エラーの発生元・エラーの種類)を比較し、両者がそれぞれ何が原因で、どちらの処理から発生しているかを確認してください。 4. 確認した内容をもとに、呼び出し元プログラムを次のとおり修正してください(1〜3の処理内容自体は変更しないこと)。 - 1の処理の呼び出しを、エラーを捕捉できる形に修正し、発生したエラーの種類に応じて次のとおり出力を切り替えること - 名前が空文字列であることに起因するエラーの場合:`名前を入力してください` - 年齢が0未満であることに起因するエラーの場合:`年齢は0以上を入力してください` - エラーが発生しなかった場合は、これまでどおり登録完了のメッセージを出力すること ### 実行例 ``` 花子を登録しました 名前を入力してください 年齢は0以上を入力してください ```