課題1601
複素数を表すクラス Complex をパッケージ j2.lesson03 に作成しなさい。
複素数とは実数と虚数を組み合わせた数のことである。
このクラスに次のようなインスタンスフィールドを用意すること。また、これらは全て private, final として指定すること。
- 実数部を表すフィールド double r
- 虚数部を現すフィールド double i
また、次のようなコンストラクタを用意すること。これは public として指定すること。
- Complex(double r, double i)
- 引数 r: この複素数の実数部を設定する
- 引数 i: この複素数の虚数部を設定する
また、次のようなインスタンスメソッドを用意すること。これらは public として指定すること。
- 加算を行うメソッド Complex add(Complex other)
- 引数 other: 足す値
- 戻り値: this と other を足した、新しい複素数インスタンス
- この操作によって、this, other に変更を加えてはならない
- 乗算を行うメソッド Complex mult(Complex other)
- 引数 other: 掛ける値
- 戻り値: this と other を掛けた、新しい複素数インスタンス
- この操作によって、this, other に変更を加えてはならない
- 複素数インスタンスを文字列で表現するメソッド String toString()
- 引数なし
- 戻り値: 実数部 a, 虚数部 b の複素数に対して "a+bi" の形で表された文字列
- new Complex(0.0, 0.0) -> "0.0+0.0i"
- new Complex(2.5, 3.1) -> "2.5+3.1i"
- new Complex(-1.0, -1.0) -> "-1.0-1.0i" ("1.0+-1.0i"でもOKとする)
- この操作によって、this, other に変更を加えてはならない
上記に記載されていないインスタンスフィールド、インスタンスメソッド、クラスメソッド、コンストラクタは必要ならば自由に用意して構わない。
なお、z = a + bi, w = c + di であるような複素数について考えた場合、次のような計算を行う。
- 加算
-
z + w = (a + c) + (b + d)i
- 乗算
-
z * w = (a * c - b * d) + (a * d + b * c)i
また、add, mult の「この操作によって、this, other に変更を加えてはならない」というのは、例えば add ならば加算を行った結果として this.r, this.i, other.r, other.i の値が変わってはいけないということである。
例えば、次のようなメソッドは間違いである。
public Complex add(Complex other) { this.r += other.r; this.i += other.i; ... }
上記の例では、this が変更されている。このようなことを避けるために、通常は計算結果から新しいインスタンスを作成し、そのインスタンスを return する。
例えば、次のように書けばよい。
public Complex add(Complex other) { double rResult = ... double iResult = ... return new Complex(rResult, iResult); }
また、toString() メソッドで文字列を作成する必要がある場合、
return a + "," + b;
上のように書くと、"(aの内容)+(bの内容)" という文字列を作ることができる。例えば、a=1.0, b=-2.0 の場合、"1.0,-2.0"という文字列が作成される。
手順
指定した箇所で必ずテストを行うこと。
- 各メソッドに対する詳細な擬似コードを作成する
- パッケージ j2.lesson03 にクラス Complex を作成
- 指定したインスタンスメソッドやコンストラクタを作成する
- テスト項目「Complexに対する骨格テスト」をパス
- インスタンスメソッドやコンストラクタの中身を書く (演習と同様)
- main メソッドを書いて簡単なテストを行う
- JUnit を用いてもよい
- テスト項目「Complexに対する単体テスト」をパス
テストの失敗メッセージ
骨格テスト
メッセージ | 詳細 |
---|---|
(クラス名), existence | パッケージ内に課題で指定したクラスが存在していない。パッケージやクラス名を確認 |
(メンバ名), existence | 指定されたメンバが存在しない |
(メンバ名), public | メンバを作る際に public の指定がない |
(メンバ名), private | メンバを作る際に private の指定がない |
(メンバ名), final | メンバを作る際に final の指定がない |
(メンバ名), not static | メンバを作る際に static が余計についている |
(メンバ名), type <T> | メンバを作る際に型の指定を間違っている。正しくは <T> |
単体テスト
メッセージ | 詳細 |
---|---|
期待された結果と異なります | (メソッド名)を起動した結果が期待された結果と異なる。テスト項目を参照 |
toString() の形式が異なります | toString() メソッドを呼び出した結果が指定された形式でない |
副作用があります | add, mult, toString を呼び出した際に値が変化している |
単体テストの項目
テスト失敗時に「Results」の欄の左側に出る「test~」は、テストの項目名を表している。
項目名 | 詳細 |
---|---|
Complex_0_0 | new Complex(0.0, 0.0).toString() |
Complex_1_0 | new Complex(1.0, 0.0).toString() |
Complex_0_1 | new Complex(0.0, 1.0).toString() |
Complex_1_1 | new Complex(1.0, 1.0).toString() |
Complex_M1_M1 | new Complex(-1.0, -1.0).toString() |
add_00 | 0 + 0 |
add_10 | 1 + 0 |
add_i0 | i + 0 |
add_01 | 0 + 1 |
add_0i | 0 + i |
mult_scale2 | (1 + 1i) * 2 |
mult_scale4 | (1 + 1i) * 2 * 2 |
mult_scale8 | (1 + 1i) * 2 * 2 * 2 |
mult_rol30 | (1 + 0i) * (0.86602540378443864676372317075294 + 0.5i)1 |
mult_rol60 | (1 + 0i) * (0.86602540378443864676372317075294 + 0.5i)2 |
mult_rol90 | (1 + 0i) * (0.86602540378443864676372317075294 + 0.5i)3 |
mult_rol120 | (1 + 0i) * (0.86602540378443864676372317075294 + 0.5i)4 |
mult_rol150 | (1 + 0i) * (0.86602540378443864676372317075294 + 0.5i)5 |
mult_rol180 | (1 + 0i) * (0.86602540378443864676372317075294 + 0.5i)6 |
mult_rol210 | (1 + 0i) * (0.86602540378443864676372317075294 + 0.5i)7 |
mult_rol240 | (1 + 0i) * (0.86602540378443864676372317075294 + 0.5i)8 |
mult_rol270 | (1 + 0i) * (0.86602540378443864676372317075294 + 0.5i)9 |
mult_rol300 | (1 + 0i) * (0.86602540378443864676372317075294 + 0.5i)10 |
mult_rol330 | (1 + 0i) * (0.86602540378443864676372317075294 + 0.5i)11 |
mult_rol360 | (1 + 0i) * (0.86602540378443864676372317075294 + 0.5i)12 |
課題1602
課題1601で作成した Complex クラスを実際に動作させるプログラム ComplexAction をパッケージ j2.lesson03 に作成しなさい。
このクラスには、mainメソッドだけを用意すればよい。
mainメソッドの動作を表す擬似コードは以下の通りである。出力する文字列などは以下の擬似コードに従うこと。
プログラム全体 print "uの実数部を入力:" a = コンソール入力 (double) print "uの虚数部を入力:" b = コンソール入力 (double) print "vの実数部を入力:" c = コンソール入力 (double) print "vの虚数部を入力:" d = コンソール入力 (double) u = 複素数 (a + bi) v = 複素数 (c + di) print "足すと" + (u と v を足した結果).toString(), 改行 print "掛けると" + (u と v を掛けた結果).toString(), 改行
- 複素数 (x + yi) で示した部分は、コンストラクタ Complex(double, double) を使用すること
- (x と y を~した結果)で示される部分には、適切なメソッド呼び出しをした結果を使用すること
結果の例
入力に順に 1.0, 2.0, 3.0, 4.0 を指定した場合、プログラムを終了まで実行した際のコンソールは以下のようになっている。
uの実数部を入力:1.0 uの虚数部を入力:2.0 vの実数部を入力:3.0 vの虚数部を入力:4.0 足すと4.0+6.0i 掛けると-5.0+10.0i
手順
指定した箇所で必ずテストを行うこと。
- 各メソッドに対する詳細な擬似コードを作成する
- パッケージ j2.lesson03 にクラス ComplexAction を作成
- クラス内に throws IOException の指定をした mainメソッド (public static void main(String[] args) throws IOException { }) を作成
- 必ず throws IOException の記述を付加しておくこと (つまり、import java.io.*;も必要となる)
- テスト項目「ComplexActionに対する骨格テスト」をパス
- 擬似コードをコメントとして貼り付ける
- mainメソッドを実装する
- 実際に実行して動作を確認する
- テスト項目 「ComplexActionに対する機能テスト」 をパス
テストの失敗メッセージ
骨格テスト
メッセージ | 詳細 |
---|---|
(クラス名), existence | パッケージ内に課題で指定したクラスが存在していない。パッケージやクラス名を確認 |
(メソッド名), existence | 指定されたメソッドが存在しない。名前や仮引数の型を確認 |
(メソッド名), public | メソッドを作る際に public が抜けている |
(メソッド名), static | メソッドを作る際に static が抜けている |
(メソッド名), type <T> | メソッドを作る際に戻り値型の指定を間違っている。正しくは <T> |
(メソッド名), throws ... | メソッドを作る際に throws... の指定がない |
機能テストの項目
テスト失敗時に「Results」の欄の左側に出る「test~」は、テストの項目名を表している。
項目名 | 詳細 |
---|---|
sample | 結果の例と同じ入力 |
課題1603 (optional)
アラームつきの24時間時計を表すクラス AlarmClock をパッケージ j2.lesson03 に作成しなさい。
演習では24時間時計を表すクラスを作成したが、ここではそれにさらにアラーム機能をつける。このアラーム機能は時刻をセットし、その時間を過ぎたりその時間になったりした際にアラームを鳴らす。
AlarmClock クラスはいくつかの private インスタンスフィールドと、1つの public コンストラクタ、3つの public インスタンスメソッドを持つ。
インスタンスフィールドはどのようなものを持ってもかまわない。ただし、すべて private で宣言すること。
コンストラクタは次のようなものを用意すること。これは public として指定すること。
- AlarmClock(int hour, int minute, int second)
- 初期時刻を設定してアラームつき時計を作成する
- 引数 hour: この時計の初期時刻のうち 時間 を設定する
- 引数 minute: この時計の初期時刻のうち 分 を設定する
- 引数 second: この時計の初期時刻のうち 秒 を設定する
- 初期状態ではアラームは設定されていない (解除された状態にある)
インスタンスメソッドは次のものを用意すること。これらは public として指定すること。
- void setAlarmFor(int hour, int minute, int second)
- アラーム時刻を設定する。すでにアラームが設定されていた場合、古いアラームは解除される。
- 引数 hour: アラーム時刻のうち 時間 を設定する
- 引数 minute: アラーム時刻のうち 分 を設定する
- 引数 second: アラーム時刻のうち 秒 を設定する
- 戻り値なし
- アラームをセットした時刻と現在時刻が同じならばアラームを鳴らし(*1)、アラームを解除する
- void tick(int n)
- 指定した秒数だけ時計の時刻を進める
- 引数 n: 進める秒数
- 戻り値なし
- 時刻を進めたことによって、アラーム時刻を通過したりアラーム時刻に達した場合、アラームを鳴らし(*1)、アラームを解除する
- void show()
- 現在時刻を表示する
- 戻り値なし
- 現在時刻の 時, 分, 秒 を h, m, s で表すとして h時m分s秒 の形式で表示し、表示後に改行を入れる
- 第16回目演習?と同じ仕様である
- *1: アラームを鳴らすべき状況では、アラーム と表示し、表示後に改行を入れる
また、実際にこのクラスを使用するための簡単な main メソッドを AlarmClock クラス内に用意すること。
手順
指定した箇所で必ずテストを行うこと。
- 各メソッドに対する詳細な擬似コードを作成する
- パッケージ j2.lesson03 にクラス AlarmClock を作成
- クラス内に必要なコンストラクタ、メソッドの骨格を作成
- テスト項目「AlarmClockに対する骨格テスト」をパス
- 擬似コードを各メソッドにコメントとして貼り付ける
- 擬似コードに対するプログラムを書く (main メソッド以外)
- 各メソッドをテストする (JUnitを用いてもよい)
- テスト項目 「AlarmClockに対する単体テスト」 をパス
- mainメソッドを実装する
- 実際に実行して動作を確認する
使用例
次のような main メソッドを書いた場合を考える。
// 23時15分 AlarmClock clock = new AlarmClock(23, 15, 0); clock.show(); // 現在の時刻にセット clock.setAlarmFor(23, 15, 0); // アラームが鳴るはず // 5時にセット clock.setAlarmFor(5, 0, 0); // 2時間進める clock.tick(2 * 3600); clock.show(); // 2時間進める clock.tick(2 * 3600); clock.show(); // 2時間進める clock.tick(2 * 3600); // アラームが鳴るはず clock.show();
これを実行すると、次のように表示される。
23時15分0秒 アラーム 1時15分0秒 3時15分0秒 アラーム 5時15分0秒
テストの失敗メッセージ
骨格テスト
メッセージ | 詳細 |
---|---|
(クラス名), existence | パッケージ内に課題で指定したクラスが存在していない。パッケージやクラス名を確認 |
(メソッド名), existence | 指定されたメソッドが存在しない。名前や仮引数の型を確認 |
(メソッド名), public | メソッドを作る際に public が抜けている |
(メソッド名), static | メソッドを作る際に static が抜けている |
(メソッド名), type <T> | メソッドを作る際に戻り値型の指定を間違っている。正しくは <T> |
単体テスト
メッセージ | 詳細 |
---|---|
期待された結果と異なります | (メソッド名)を起動した結果が期待された結果と異なる。テスト項目を参照 |
未初期化フィールド | インスタンスを代入すべきフィールドにインスタンスを代入していない可能性がある |
単体テストの項目
テスト失敗時に「Results」の欄の左側に出る「test~」は、テストの項目名を表している。
項目名 | 詳細 |
---|---|
Ctor_000 | new AlarmClock(0, 0, 0).show() |
Ctor_123 | new AlarmClock(1, 2, 3).show() |
tick_000001 | new AlarmClock(0, 0, 0).tick(1), show() |
tick_000030 | new AlarmClock(0, 0, 0).tick(30), show() |
tick_000059 | new AlarmClock(0, 0, 0).tick(59), show() |
tick_000100 | new AlarmClock(0, 0, 0).tick(60), show() |
tick_003000 | new AlarmClock(0, 0, 0).tick(1800), show() |
tick_005959 | new AlarmClock(0, 0, 0).tick(3599), show() |
tick_010000 | new AlarmClock(0, 0, 0).tick(3600), show() |
tick_120000 | new AlarmClock(0, 0, 0).tick(43200), show() |
tick_235959 | new AlarmClock(0, 0, 0).tick(86399), show() |
tick_loop | new AlarmClock(23, 59, 59).tick(1), show() |
tick_maxint | new AlarmClock(0, 0, 1).tick(1000000000).tick(1147483648), show() |
alarm_0 | new AlarmClock(0, 0, 0), setAlarmFor(0, 0, 0), show() |
alarm_1a | new AlarmClock(0, 0, 0), setAlarmFor(0, 0, 1), show() |
alarm_1b | new AlarmClock(0, 0, 0), setAlarmFor(0, 0, 1), tick(1), show() |
alarm_1c | new AlarmClock(0, 0, 0), setAlarmFor(0, 0, 1), tick(2), show() |
alarm_f | new AlarmClock(10, 0, 0), setAlarmFor(15, 0, 0), show() |
alarm_fM1 | new AlarmClock(10, 0, 0), setAlarmFor(15, 0, 0), tick(5 * 3600 - 1), show() |
alarm_f0 | new AlarmClock(10, 0, 0), setAlarmFor(15, 0, 0), tick(5 * 3600), show() |
alarm_f1 | new AlarmClock(10, 0, 0), setAlarmFor(15, 0, 0), tick(5 * 3600 + 1), show() |
alarm_b | new AlarmClock(22, 0, 0), setAlarmFor(7, 0, 0), show() |
alarm_bM1 | new AlarmClock(22, 0, 0), setAlarmFor(7, 0, 0), tick(9 * 3600 - 1), show() |
alarm_b0 | new AlarmClock(22, 0, 0), setAlarmFor(7, 0, 0), tick(9 * 3600), show() |
alarm_b1 | new AlarmClock(22, 0, 0), setAlarmFor(7, 0, 0), tick(9 * 3600 + 1), show() |
alarm_nextM1 | new AlarmClock(23, 59, 58), setAlarmFor(0, 0, 0), tick(1), show() |
alarm_next0 | new AlarmClock(23, 59, 58), setAlarmFor(0, 0, 0), tick(2), show() |
alarm_next1 | new AlarmClock(23, 59, 58), setAlarmFor(0, 0, 0), tick(3), show() |
課題1604 (optional)
整数の集合を表すクラス IntegerSet をパッケージ j2.lesson03 に作成しなさい。
この整数の集合には 0 以上 100 以下の値を格納することができる。集合とは重複のない要素の集まりのことで、例えば {1, 2, 3} からなる集合に 5 を加えると {1, 2, 3, 5} となり、そこに 2 を加えると {1, 2, 3, 5} となる (2は重複するので変化しない)。
インスタンスフィールドはどのようなものを持ってもかまわない。ただし、すべて private で宣言すること。
コンストラクタは次のようなものを用意すること。これは public として指定すること。
- IntegerSet()
- 0から100までの値を格納できる整数の集合を作成する
インスタンスメソッドは次のものを用意すること。これらは public として指定すること。
- void add(int n)
- この集合に値を追加する。すでに指定した値が存在する場合は何もしない。
- 引数 n: 追加する値
- 戻り値なし
- void remove(int n)
- この集合から値を取り除く。指定した値が存在しない場合は何もしない。
- 引数 n: 取り除く値
- 戻り値なし
- boolean contains(int n)
- この集合に指定した値が存在するかどうか調べる。
- 引数 n: 調べる値
- 戻り値: 存在すれば true, しなければ false
- int size()
- この集合に格納されている値の個数を返す。初期状態では 0, 最大で101
- 戻り値: 格納されている値の個数
- IntegerSet union(IntegerSet other)
- この集合と引数に与えられた集合の和集合(*1)を新しく作成する。この操作によってもとの集合に副作用はない。
- (*1) どちらかが持つ要素を全て含む集合。{1, 2, 3, 4} union {2, 4, 6, 8} => {1, 2, 3, 4, 6, 8}
- 引数 other: 和を取る集合
- 戻り値: this と other の和集合を表す新しい IntegerSet のインスタンス
- IntegerSet intersection(IntegerSet other)
- この集合と引数に与えられた集合の積集合(*1)を新しく作成する。この操作によってもとの集合に副作用はない。
- (*1) お互いが共通して持つ要素のみからなる集合。{1, 2, 3, 4} intersection {2, 4, 6, 8} => {2, 4}
- 引数 other: 積を取る集合
- 戻り値: this と other の積集合を表す新しい IntegerSet のインスタンス
- int[] toIntArray()
- この集合を整数型の配列に変換して返す。この集合に含まれる値が返される配列に昇順に格納される。返される配列の長さは this.size() に等しい。
- 戻り値: この集合を整数型の配列で表したもの
また、実際にこのクラスを使用するための簡単な main メソッドを IntegerSet クラス内に用意すること。
手順
指定した箇所で必ずテストを行うこと。
- 各メソッドに対する詳細な擬似コードを作成する
- パッケージ j2.lesson03 にクラス IntegerSet を作成
- クラス内に必要なコンストラクタ、メソッドの骨格を作成
- テスト項目「IntegerSetに対する骨格テスト」をパス
- 擬似コードを各メソッドにコメントとして貼り付ける
- 擬似コードに対するプログラムを書く (main メソッド以外)
- 各メソッドをテストする (JUnitを用いてもよい)
- テスト項目 「IntegerSetに対する単体テスト」 をパス
- mainメソッドを実装する
- 実際に実行して動作を確認する
ヒント
このクラスの実装にはいくつかの方法があるが、すぐに思いつくであろう項目について紹介する。
内部表現 (フィールドの作り方) によってはこのクラスは簡単に作成できるが、逆に内部表現を間違えると非常に長いプログラムになってしまう可能性がある。
カプセル化の利点は、内部表現を隠蔽することによって、実際の挙動と内部表現が大きく違っても問題がなくなるため、より単純な実装をすることができるという点である。
int[] を使って集合を表現する場合
0,..,100 の数値を格納するために int 配列型を用いるという方法が挙げられる。
add を行う際に順番に配列に格納していくという方法が標準的であると思われる。
この方法では、add メソッドを次のように定義する。
public void add(int n) { ... this.set[size] = n; this.size++; }
ただし、このクラスは「集合」を表すため、重複要素が入らないように注意する必要がある。重複要素を許してしまうと、この配列には無限個の整数を格納できる必要ができてしまう。
また、remove, contains メソッドでは、対象の数値が存在するかどうか検索する必要がある。
boolean[] を使って集合を表現する場合
より良い方法して、boolean 配列を用いるという方法が挙げられる。
この方法では、contains メソッドを次のように定義する (set は boolean[] 型のフィールド)。
public boolean contains(int n) { ... return this.set[n]; }
つまり、整数 n がある IntegerSet のインスタンス s に含まれている場合、s.set[n] = true で表す。逆に n が含まれていない場合は s.set[n] = false で表す。
このように定義すると、s.set の各要素は「存在する」か「存在しない」かのどちらかを表すだけになり、重複要素について特に難しく考える必要がなくなる。
テストの失敗メッセージ
骨格テスト
メッセージ | 詳細 |
---|---|
(クラス名), existence | パッケージ内に課題で指定したクラスが存在していない。パッケージやクラス名を確認 |
(メソッド名), existence | 指定されたメソッドが存在しない。名前や仮引数の型を確認 |
(メソッド名), public | メソッドを作る際に public が抜けている |
(メソッド名), static | メソッドを作る際に static が抜けている |
(メソッド名), type <T> | メソッドを作る際に戻り値型の指定を間違っている。正しくは <T> |
単体テスト
メッセージ | 詳細 |
---|---|
期待された結果と異なります | (メソッド名)を起動した結果が期待された結果と異なる。テスト項目を参照 |
未初期化フィールド | インスタンスを代入すべきフィールドにインスタンスを代入していない可能性がある |
範囲外のインデックスにアクセス | 配列の範囲外のインデックスにアクセスしている。配列の初期サイズを確認 |
副作用があります | union, intersection を呼び出した際に値が変化している |
単体テストの項目
テスト失敗時に「Results」の欄の左側に出る「test~」は、テストの項目名を表している。
項目名 | 詳細 |
---|---|
add_0 | 空の集合に対して add(0) |
add_50 | 空の集合に対して add(50) |
add_100 | 空の集合に対して add(100) |
remove_0 | {0,..,100} に対して remove(0) |
remove_50 | {0,..,100} に対して remove(50) |
remove_100 | {0,..,100} に対して remove(100) |
contains_f0 | 空の集合に対して contains(0) |
contains_f50 | 空の集合に対して contains(50) |
contains_f100 | 空の集合に対して contains(100) |
contains_t0 | {0,..,100} に対して contains(0) |
contains_t50 | {0,..,100} に対して contains(50) |
contains_t100 | {0,..,100} に対して contains(100) |
size_0 | 空の集合に対して size() |
size_1 | {0} に対して size() |
size_34 | {0,3,6,..,96,99} に対して size() |
size_51 | {0,2,4,..,98,100} に対して size() |
size_101 | {0,..,100} に対して size() |
union_0_0 | {} union {} |
union_0_100 | {} union {0,..,100} |
union_100_0 | {0,..,100} union {} |
union_100_100 | {0,..,100} union {0,..,100} |
union_x2_x3 | {0,2,4,..,98,100} union {0,3,6,..,96,99} |
intersection_0_0 | {} intersection {} |
intersection_0_100 | {} intersection {0,..,100} |
intersection_100_0 | {0,..,100} intersection {} |
intersection_100_100 | {0,..,100} intersection {0,..,100} |
intersection_x2_x3 | {0,2,4,..,98,100} intersection {0,3,6,..,96,99} |
toIntArray_0 | {}.toIntArray() |
toIntArray_x1 | {0,..,100}.toIntArray() |
toIntArray_x2 | {0,2,..,98,100}.toIntArray() |
toIntArray_x3 | {0,3,..96,99}.toIntArray() |
ramdom | ランダムに値を追加して、ランダムに値を削除して、toIntArray() |