課題の進め方

基本的には適当なプロジェクト/ソリューションを作って課題を進めた上で,Program.csのみ(特に他に指示がなければ)を提出する.

他の人(受講者・非受講者両方)に解答内容(一部でも)を見せない,そして他の人の解答内容(一部でも)を見ないようお願いします.特に,公開の場所に解答を置かないようお願いします.githubやbitbucket等は使える人は使えばよいと思いますが,privateレポジトリにするようにお願いします.

プロジェクト/ソリューションの作成

Note

再掲:プロジェクトは一つの実行形式やライブラリを作成するためのコード等を全てまとめたものであり,ソリューションは関連するプロジェクトをまとめたもの(参考:What are solutions and projects in Visual Studio).

適当な名前(たとえば課題3の解答なのでQ3にするなど)のフォルダを適当な場所に作成し, VSCodeで作成したフォルダを開く.そして,VSCode内のターミナルで以下を実行する.

dotnet new console -o .

課題の実施

Program.cs を問題文の指示の通りに編集する(提出・採点手続きの簡略化のため提出する.csファイルは一つのみ).作成した.csファイルには先頭部分に学籍番号と名前をコメントとして含めること.なので,たとえば学籍番号Z0TB9999の東北 大学さんの提出ファイルは

// Z0TB9999
// 東北 大学

という行から始まる.

提出

できあがった Program.csをClassroom内の当該回の「課題」より提出する.最初のステップで作成したフォルダにあるはず.また問題文に指示がある場合はそのファイル(例:課題4のitems.txt)も提出する.提出前には以下を確認しよう.

基本課題

その1

Dictionary<string, int>型のオブジェクトにキーとバリューの組をファイルから読みとることで追加したい.具体的には,中身が

Chocolate,2
Chips,4
Candy,5

であるファイルを読んで,今着目しているディクショナリdict

dict["Chocolate"] = 2; 
dict["Chips"]     = 4; 
dict["Candy"]     = 5; 

としたのと同等の要素を追加したい.

上記を達成するため,以下のプログラムの空欄を埋めて完成させよ.

// あなたの学籍番号
// あなたの名前

using System;
using System.IO; 
using System.Collections.Generic;

// インベントリ(いわゆる「アイテム欄」)
class Inventory
{
      public Dictionary<string, int> Items { get; private set; } = new Dictionary<string, int>();

      public void AddItemsFromFile(string filepath)
      {
         // 実装する.
         //
         // 与えられたパス filepath にファイルが存在し,そのファイルの中身が,各行が
         // 
         //    アイテム名,個数
         //
         // という形式であるようなテキストファイルになっているときに,
         // 各行に含まれているアイテム名と個数の対応をディクショナリ Items に追加する.
         // より詳細な形式については後述
      }

      // 必要に応じて 上の AddItemsFromFile(string) から呼ぶためのメソッドを追加してもよい.

      // 変更しない
      public void PrintAll()
      {
         foreach (var k in Items.Keys)
         {
            Console.WriteLine(k + " × " + Items[k]);
         }
      }
}

// 変更しない
class Program
{
      static void Main()
      {
         Inventory i = new Inventory();
         i.AddItemsFromFile("./items.txt");
         i.PrintAll();
      }
}

items.txtの中身の例

Chocolate,2
Chips,0
Candy,5

そのときの期待される出力

Chocolate × 2
Chips × 0
Candy × 5

入力ファイルの形式

items.txtも提出物に含めること(発展課題も同じ).含めるitems.txtは自身がプログラムの挙動を確認するのに使用したものでよい.

Note

items.txtを相対パス./items.txtでアクセスするためには,items.txtは作業フォルダ直下に置く必要がある.環境設定1で紹介した方法でプロジェクトを作成しプログラムを実行しているのならば,VS Codeで開いたフォルダ(プロジェクトルート)直下に置いたので問題がないはずだ.

そうでない場合(Visual Studioを使用している場合など)は以下のいずれかを行う.

  • .csprojの<PropertyGroup>...</PropertyGroup>内に以下の行を追加する.

         <StartWorkingDirectory>$(MSBuildProjectDirectory)</StartWorkingDirectory>
  • Windows版のVisual Studioを使用している場合は,プロジェクト名を右クリックして出てくるコンテキストメニューから「プロパティ」を選択し,「デバッグ」の「作業ディレクトリ」を当該 .csproj ファイルの直上のディレクトリに設定する.

  • Mac版のVisual Studioを使用している場合は,プロジェクト名をダブルクリックして出てくるウィンドウの「実行」>「構成」> 「Default」の 「作業ディレクトリ」を当該 .csproj ファイルの直上のディレクトリに設定する.

Tip

個数の部分を処理するには,Int32.Parse(string)を使うとよい.

この関数の使い方は以下のプログラムを参考にせよ.

using System; 

// 参考:https://docs.microsoft.com/en-us/dotnet/api/system.int32.parse?view=net-6.0#system-int32-parse(system-string)
class Int32Example 
{
    static void TryParseInt(string s) 
    {           
        try 
        {
            int n = Int32.Parse(s); 
            Console.WriteLine(s + " ==> " + n);
        }
        catch(FormatException e)
        {
            Console.WriteLine(s + ": ill-formed");
        }
        catch(OverflowException e)
        {
            Console.WriteLine(s + ": overflow");
        }
        catch(ArgumentNullException e) 
        {
            Console.WriteLine("the input is null");
        }
    }

    static void Main() 
    {
        TryParseInt("1234");
        TryParseInt("-1234");
        TryParseInt("0xbeef");
        TryParseInt("+34");
        TryParseInt("3e10");
        TryParseInt("011"); 
        TryParseInt("1,000");
        TryParseInt(string.Join("", new string[] { "1", "000", "000", "000", "000" }));
    }
}

上のコードの出力

1234 ==> 1234
-1234 ==> -1234
0xbeef: ill-formed
+34 ==> 34
3e10: ill-formed
011 ==> 11
1,000: ill-formed
1000000000000: overflow

その2

基本課題その1で作成したAddItemsFromFile(string)を拡張し,入力テキストファイルに同じアイテム名が複数含まれていた場合にそれらの個数を合計するようにせよ.

items.txtの中身の例

Chocolate,2
Chips,14
Candy,5
Chocolate,40
Chips,-14

そのときの期待される出力

Chocolate × 42
Chips × 0
Candy × 5

Tip

ディクショナリにキーが含まれているかどうかを確認するにはContainsKey(TKey)メソッドを用いることができる.たとえば式

dict.ContainsKey( k ) 

の評価結果はkdictに含まれていればtrue,そうでなければfalseである.

発展課題

Important

本課題を完了できたのならば本課題の解答のみを提出すればよく,基本課題の解答は提出する必要はない.

基本課題で作成したAddItemsFromFile(string)を拡張し,入力テキストファイルにコメントや余計な空白,空行が入っていても正常に読みこめるようにせよ.ここで,"#"で始まる行をコメントとする("#"が行頭でない場合はコメントでないのに注意).また,空行は空白を含んでいてもよい.また,ある行において指定されたフォーマットで解釈することに失敗した場合には,Itemsはその行以前までの行を読み取った結果で更新されるようにせよ.ここで,指定されたフォーマットで解釈できない行とは,たとえば以下のようなものである.

items.txtの中身の例1

#コメント行
Chocolate,  2
   Chips ,14  

Candy,5
Chocolate,40

例1に対し期待される出力

Chocolate × 42
Chips × 14
Candy × 5

items.txtの中身の例2

# コメント行
Chocolate,  2
   Chips ,14  
# 空行は空白を含んでいてもよい
      

Candy,5
Chocolate,40
      #コメントでない行
Candy,25

例2に対し期待される出力

Chocolate × 42
Chips × 14
Candy × 5

items.txtの中身の例3

# アイテム名が数字なのは許される
100 , 5
# アイテム名に#は許される
 ####  , 9
# 個数の部分にはInt32.Parse()が失敗するようなものは許さない
something,ten

例3に対し期待される出力

100 × 5
#### × 9