課題の進め方

基本的には適当なプロジェクト/ソリューションを作って課題を進めた上で,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ファイルはProgram.csのみ).作成した.csファイルには先頭部分に学籍番号と名前をコメントとして含めること.なので,たとえば学籍番号Z0TB9999の東北 大学さんの提出ファイルは

// Z0TB9999
// 東北 大学

という行から始まる.

提出

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

    • ただし,友人の解答を見ない(一部でも),そして友人に解答を見せない(一部でも)ようお願いします

基本課題

以下の内容のCalc.csを考える.

// Calc.cs
using System;

// 以下のクラスは変更してはならない.
// 電卓を表す
class Calc
{
   // 現在の値
   protected double val;

   public Calc()
   {
      val = 0;
   }

   public virtual void Set(double d)
   {
      val = d;
   }
   public virtual void Add(double d)
   {
      val += d;
   }
   public virtual void Sub(double d)
   {
      val -= d;
   }
   public virtual void Mul(double d)
   {
      val *= d;
   }
   public virtual void Div(double d)
   {
      val /= d;
   }
   public void Report()
   {
      Console.WriteLine("Value: " + val);
   }
}

このCalc.csProgram.csと同じフォルダに置くものとし,以後変更しない.提出するのはProgram.csのみでありCalc.csは提出物に含めない

このCalcクラスを継承し,以下のメソッドを備えた関数電卓クラスFunCalcを作成せよ.ただし,Calcクラスを変更してはならない.

メソッド 説明
public void Pow(double d) 「現在の値」をd乗にする.
public void SetLogBase(double d) 「logの底」をdに設定する.「現在の値」は変更しない
public void Log() 「現在の値」をそのlog(底は「logの底」)に設定する.
public void SetRadianMode() 「角度の単位」をラジアンに設定する.「現在の値」は変更しない
public void SetDegreeMode() 「角度の単位」を度に設定する.「現在の値」は変更しない
public void Sin() 「現在の値」を角度(度かラジアンかは「角度の単位」による)として,そのsinに「現在の値」を設定する.
public void Cos() 「現在の値」を角度(度かラジアンかは「角度の単位」による)として,そのcosに「現在の値」を設定する.

これらのメソッドを実装するためにFunCalcは適当なフィールド(ひとつとは限らない)を持つ必要がある.そうしたフィールドはpublicにはしないようにすること.また,度をラジアンに直す等の共通の処理はprivateなメソッドにまとめるとよい.なお,「角度の単位」の初期値については問わない.

提出するプログラムは以下のクラスProgramを持つこと.

class Program
{
      static void RepeatDiv(Calc c, double d, int n) {
         for (int i = 0; i < n; i++) {
            c.Div(d); 
         }
      }
      static void Main(string[] args)
      {
         FunCalc fc = new FunCalc();

         // 16 / 2 / 2 / 2 / 2 
         fc.Set(16); 
         RepeatDiv(fc,2,4); // fcはCalcのインスタンスとしても使用可
         fc.Report();

         // sin(30°)の計算
         fc.SetDegreeMode();
         fc.Set(30);
         fc.Sin();
         fc.Report();

         // sin(30°)の計算(少し別の方法)
         fc.Set(30);
         fc.SetDegreeMode();
         fc.Sin();
         fc.Report();

         // cos(π)の計算
         fc.SetRadianMode();
         fc.Set(Math.PI);
         fc.Cos();
         fc.Report();

         // SetDegreeMode()およびSetRadianMode()は「現在の値」には影響しない.
         fc.Set(10);
         fc.SetDegreeMode();
         fc.SetDegreeMode();
         fc.Report();
         fc.SetRadianMode();
         fc.SetRadianMode();
         fc.Report();

         // 2^10
         fc.Set(2);
         fc.Pow(10);
         fc.Report();

         // log₂(2^10)
         fc.SetLogBase(2);
         fc.Log();
         fc.Report();

         // log₁₀(log₂(2^10))
         fc.SetLogBase(10);
         fc.Log();
         fc.Report();
      }
}

期待される出力の例

Value: 1 
Value: 0.49999999999999994
Value: 0.49999999999999994
Value: -1
Value: 10
Value: 10
Value: 1024
Value: 10
Value: 1

Note

sinやcos,logや累乗を計算するのにはMathクラスのstaticメソッドSin(double)Cos(double)Log(double,double)Pow(double,double)が利用できる. πについてもMath.PIが利用可能である.詳細は,.NET API リファレンスの関連ページを参照のこと.特に,LogPowがどっちの引数が何を表しているかをちゃんと確認するとよい.

また言うまでもないかもだが,180° = πラジアン である.

発展課題

Important

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

基本課題で作成した FunCalcの一部の関数を変更し,不正な値が与えらえたときにエラーとして扱い,計算等はせず(「現在の値」等の状態も変更しない)に Error! と表示するようにせよ. 具体的には以下のようにメソッドの挙動を変更せよ.ただし,Calcクラスを変更してはならない.

メソッド 変更点
SetLogBase(double d) dが0以下か,1であればエラーとする
Log() メソッド呼び出し時点においてvalが0以下であればエラーとする
Div(double d) dが0であればエラーとする

Program.Mainにたとえば以下のような記述を追加し動作確認するとよい(既存の処理を削除してはならない).

class Program
{
      // … RepeatDivの定義は基本課題と同じため割愛 …
      static void Main(string[] args)
      {
         FunCalc fc = new FunCalc();

         // … 基本課題と同じ部分については省略 …

         // エラー処理の動作確認用
         fc.Div(0);           // Error!
         fc.Report();         // (直前のfc.Report()と同じ値が表示される)
         RepeatDiv(fc, 0, 4); // Error! が4回表示される
         fc.Set(0);
         fc.Log();          // Error!
         fc.Report();       // Value: 0
         fc.SetLogBase(-1); // Error!
         fc.SetLogBase(1);  // Error!
      }
}

ヒント: 一部の実装にはオーバライドを使う?