class Complex
Download
Report
Transcript class Complex
計算機プログラミングI
第10回
• データ構造とクラスの設計
–例題:
• 複素数
• 図形要素
• Morphing
–話題:
• 複雑な値を一つにまとめる
• 似たような値を統一的に扱う
• 中身を気にしないで扱いたい
1
計算機プログラミングI (増原) 2003年度
複雑な値を一つにまとめる
• プログラム中で扱うモノ
整数・タートル・複素数・図形要素・・・
• 複数の値の組み合わせで表わされるモノも多い
–タートル = X,Y座標,方向,色,ペン状態,…
–複素数 = 実・虚成分
–直線 = 両端点のX,Y座標
• 各値をバラバラに扱うのは大変
→ 1つの値として扱いたい (変数に代入・引数・・・)
→ オブジェクトとして表現
2
計算機プログラミングI (増原) 2003年度
例題: マンデルブロ集合を表示したい
• 直感的なアルゴリズム:
オブジェクトを用いない
複素関数 f(c,z) = z2+c が
プログラム
for(c_real=…)
与えられた場合
for(c_imag=…)
• 複素平面上の各cについて
z_real=0, z_imag=0;
for (k=0;k<30;k++) {
– z0=0としてi=0,1,2,…,29
mag = sqrt(z_real*z_real
まで繰り返す
+z_imag*z_imag);
• |zi|>2となったら
if (mag<2) break;
z_sq_real=z_real*z_real
繰り返しを止める
-z_imag*z_imag;
• zi+1=zi2+cとする
z_sq_imag=2*z_real*z_imag;
z_real = z_sq_real+c_real;
–繰り返しの回数に
z_imag = z_sq_imag+c_imag;
応じた色をc上に塗る
}
3
計算機プログラミングI (増原) 2003年度
複素数をクラスとして表わす
• 「複素数」という1つの数を
「1つのオブジェクト」として表わそう!
クラスを定義する
• 複素数という1つの数を簡単に作れるようにしよう
コンストラクタを定義する
• 複素数に関する計算は複素数クラスに定義しよう
インスタンスメソッドを定義する
• 複素数の表現形式を使い分けよう
継承を利用する
4
計算機プログラミングI (増原) 2003年度
複素数: クラスの定義
• 複素数オブジェクトの性質の定義
= クラスの定義
• 名前 : Complex
/** 複素数 */
class Complex {
インスタンス変数・
インスタンスメソッド・
コンストラクタの定義
}
5
計算機プログラミングI (増原) 2003年度
複素数: インスタンス変数の定義
• 複素数のベクトル表現: x+yi
→ 2つの実数
→ 2つの実数型インスタンス変数
class Complex {
public double real; // 実部
public double imag; // 虚部
インスタンスメソッド・
コンストラクタの定義
}
Complex c = new Complex();
c.real = 1;
c.imag = 2;
6
計算機プログラミングI (増原) 2003年度
複素数:コンストラクタの定義
• Complex c = new Complex(1,2); のようにしたい
class Complex {
public double real; // 実部
public double imag; // 虚部
/** 実部, 虚部から複素数を作る */
}
public Complex(double r, double i) {
real = r;
imag = i;
}
7
計算機プログラミングI (増原) 2003年度
複素数の演算とメソッド
• 複素数どうしの演算 (乗算、加算、etc.) を
どのように定義するか? (例: z*c)
• 方針1: 新しく複素数の値を作って返す
–例: zとcの積は新しいオブジェクト。zやcはそのまま
–数学的。z*c+(z*z+c)のような計算が素直に書ける
• 方針2: メソッド呼び出しを受けたオブジェクトの
状態を変化
–例: zの新しい状態がz*cになる
–オブジェクトが何かの実体に対応している場合に便利
(例: タートル・図形要素・etc.)
8
計算機プログラミングI (増原) 2003年度
複素数:インスタンスメソッドの定義
class Complex {
インスタンス変数・
コンストラクタの定義
public Complex add(Complex y) {
double r = real + y.real; //実部の計算
double i = imag + y.imag; //虚部の計算
return new Complex(r,i); //新しい複素数を作って返す
}
}
Complex c1 = new Complex(1,2);
Complex c2 = new Complex(3,4);
Complex c3 =
c1.add(c2);
9
計算機プログラミングI (増原) 2003年度
例題: マンデルブロ集合
オブジェクトを用いた
プログラム
オブジェクトを用いない
プログラム
for(c_real=…)
for(c_imag=…)
z_real=0, z_imag=0;
for (k=0;k<30;k++) {
mag = sqrt(z_real*z_real
+z_imag*z_imag);
if (mag<2) break;
z_sq_real=z_real*z_real
-z_imag*z_imag;
z_sq_imag=2*z_real*z_imag;
z_real = z_sq_real+c_real;
z_imag = z_sq_imag+c_imag;
}
for(c_real=…)
for(c_imag=…)
c = new Complex(c_real,c_imag);
z = new Complex(0,0);
for (k=0;k<30;k++) {
if (z.mag()<2) break;
z = z.mul(z).add(c);
}
10
計算機プログラミングI (増原) 2003年度
似たような値を統一的に扱う
• 異なるけど統一的に扱いたい値
–例: 図形要素の線と円は違うモノだけど
「表示する」「距離を求める」等は同様に行いたい
→ 継承を活用する
–共通する性質・操作は親クラスに定義
–個別の操作内容は子クラスのメソッドで再定義
–個別の性質・操作は子クラスに(追加定義)
→ 「同様に行いたい」部分は親クラスのオブジェク
トとして扱うことができる
11
計算機プログラミングI (増原) 2003年度
図形要素
• 図形エディタの中の個々の要素 (線や円など)を
どのようにして扱うか? ―― 行う操作を考える
– 例: マウスによる消去
1. 画面上の1点を選ぶ
2. その点に最も近い図形要素を見つける
3. 画面を消す
4. 全ての図形要素(ただし2で見つけたもの以外)を再描画する
• 図形要素を単位として操作→オブジェクト
• 統一して行いたい(だけど図形ごとに違う)操作:
– 距離を求める
– 画面に表示する
12
計算機プログラミングI (増原) 2003年度
共通図形要素のクラス定義
• とりあえず共通するクラスを作り、
共通して存在すべき操作をメソッドとして定義
public class FigureElement {
public void draw(Turtle m) {}
public double distance(int x, int y) {
return 0;
}
}
13
計算機プログラミングI (増原) 2003年度
個々の図形要素のクラス定義
public class FigureElement {
public void draw(Turtle m) {}
public double distance(int x, int y) {
return 0;
}
}
public class Point
extends FigureElement {
int x, y;
public void draw(Turtle m) { … }
public double distance(int x, int y) {
…;
}
}
public class Line
extends FigureElement {
インスタンス変数
public void draw(Turtle m) { … }
public double distance(int x, int y) {
…;
}
}
14
計算機プログラミングI (増原) 2003年度
図形要素を使う側のプログラム
FigureElement[] figs = …;
int numFigs = …;
/* x,y に最も近い図形を見つける*/
double minDist = MAX;
for (int i=0; i<numFigus; i++) {
if (figs[i].distance(x,y)
< minDist) {
…} }
//見つけた図形要素を取り除く(略)
//再描画
for (int i= …) figs[i].draw(m);
public class FigureElement {
public void draw(Turtle m) {}
public double distance(int x, int y) {
return 0;
}
}
• 使う側はどのような
図形要素かを気にせず
に距離を求め、再描画
できる。
• 図形要素が増えても、
この部分は変更不用!
15
計算機プログラミングI (増原) 2003年度
何が共通? 何が個有?
public class FigureElement {
public void draw(Turtle m) {}
public double distance(int x, int y) {
return 0;
}
}
public class Point
extends FigureElement {
int x, y;
public void draw(Turtle m) { … }
public double distance(int x, int y) {
…;
}
}
• 色: 全てに共通
– FigureElementのインスタ
ンス変数
– 個々のdrawメソッド
• 回転・拡大: 設計による
1. 回転角や拡大率を共通の
public class Line
性質として持たせる
extends FigureElement {
2. 拡大すると個々の状態(例
インスタンス変数
えば線分の座標)を変化
public void draw(Turtle m) { … }
– 時にはクラスの設計を1か
public double distance(int x, int y) {
らやり直す必要も
…;
}
}
16
計算機プログラミングI (増原) 2003年度
中身を気にしたくない
• 複雑な値の詳細を気にせずに扱いたい
–例: タートルの座標が整数か実数か
–例: 複素数は直交座標か極形式か
–例: 直線は「両端点」か「1点と幅・高さ」か「1点と方向・
長さ」か
→ インスタンスメソッドを通してのみ
オブジェクト状態を得るようにプログラムを書くと
クラス定義の中身を変えても大丈夫
17
計算機プログラミングI (増原) 2003年度
一つの値、二つの表現
• 複素数の表わし方には二つある:
–直交座標系: 実部と虚部の成分の組 x+iy
– 極形式: reiq
y
r
q
x
18
計算機プログラミングI (増原) 2003年度
2つの複素数クラス
• 直交座標系
• 極形式
class Complex {
class Complex {
double real, imag;
double mag, theta;
double getReal() {return real;}
double getReal() { … }
double getImag() {return real;}
double getImag() { … }
double getMag() { … }
double getMag() { return mag; }
double getAng() { … }
double getAng() { return theta; }
Complex add(Complex c) { … }
Complex add(Complex c) { … }
使う側は同じプログラムでOK
Complex mul(Complex for(c_real=…)
c) { … }
Complex mul(Complex c) { … }
for(c_imag=…)
…
…
c = new Complex(c_real,c_imag);
}
}
z = new Complex(0,0);
for (k=0;k<30;k++) {
if (z.mag()<2) break;
z = z.mul(z).add(c);
}
19
計算機プログラミングI (増原) 2003年度
一つの値、二つの表現
• 複素数の表わし方には二つある:
–直交座標系: 実部と虚部の成分の組 x+iy
– 極形式: reiq
• それぞれ向き、不向きがある
–直交座標系: 和・差 (成分ごと)
–極形式: 積・商・絶対値
(絶対値の積・商,偏角の和・差)
• 二つの表現のイイトコドリをしたい!
→ 2つの複素数クラス
y
r
q
x
20
計算機プログラミングI (増原) 2003年度
一つの値、二つの表現
• 複素数の表わし方には二つある:
–直交座標系: 実部と虚部の成分の組 x+iy
– 極形式: reiq
• それぞれ向き、不向きがある
–直交座標系: 和・差 (成分ごと)
–極形式: 積・商・絶対値
(絶対値の積・商,偏角の和・差)
• 二つの表現のイイトコドリをしたい!
→ 2つの複素数クラス
y
r
q
x
21
計算機プログラミングI (増原) 2003年度
2つの複素数クラス
•直交座標系
class Comlpex {
double getReal() {…}
double getImag() {…}
double getMag() {…}
double getAng() {…}
Complex add(Complex c) { … }
…
}
class CartesianComplex
extends Complex {
double real, imag;
double getReal() { … }
}
•共通の性質・操作
• 実部・虚部などを
求める
• 四則演算
•極形式
class PolarComplex
extends Complex {
double mag, theta;
dobule getReal() { … }
}
22
計算機プログラミングI (増原) 2003年度
2つの複素数クラス
•直交座標系
class Comlpex {
…略…
Complex add(Complex c) {
return new CartesianComlpex(
this.getReal()+c.getReal(),
this.getImag()+c.getImag());}
Complex mul(Complex c) {
return new PolarComplex(
this.getMag()*c.getMag(),
this.getAng()+c.getAng());}
}
class CartesianComplex
extends Complex {
double real, imag;
double getReal() {
return real;}
}
•共通の性質
• 実部・虚部などを
求める――個別
• 四則演算――
共通
•極形式
class PolarComplex
extends Complex {
double mag, theta;
dobule getReal() {
return mag*cos(theta);}
}
計算機プログラミングI (増原) 2003年度
23
2つの複素数を使う人は?
Complex c1 =
new CatesianComplex(1,2);
Complex c2 =
new PolarComplex(0,PI/2);
Complex c3 =
c1.add(c2).mul(c2);
if (c3.getMag() < 2) …
class Comlpex {
double getReal() {…}
double getImag() {…}
double getMag() {…}
double getAng() {…}
Complex add(Complex c) { … }
…
}
• 使う人はどっちの表現
かを気にしなくてもよい!
24
計算機プログラミングI (増原) 2003年度
おまけ
• 継承でひろがる可能性
25
計算機プログラミングI (増原) 2003年度
Morphing再び
• Morphingのためのコード
for(i = 0; i < steps; i++){
mc (円)を移動
ms (星形)を移動
mm.moveTo((mc.getX()+ms.getX())/2,
(mc.getY()+ms.getY())/2,
0);
}
• 中間の位置を計算し、
その位置へタートルを移動させる
26
計算機プログラミングI (増原) 2003年度
オブジェクト指向によるMorphing
• 中間の図形を描くタートルは
–追いかけるタートルを知っている
–追いかけろ、と指示を受けると
自ら中間地点へ移動
for(i = 0; i < steps; i++){
mc (円)を移動
ms (星形)を移動
mm.step();
}
星
円
Morph
27
計算機プログラミングI (増原) 2003年度
Morphクラスの設計
• Turtleクラスを拡張
• 追いかける対象
public class Morph
extends Turtle {
→インスタンス変数
// 追いかける対象
Turtle m1, m2;
コンストラクタ・インスタンスメソッドの定義
}
28
計算機プログラミングI (増原) 2003年度
Morph: コンストラクタ
• Morph m = new Morph(mCircle, mStar);
のようにしたい
public class Morph extends Turtle {
public Turtle m1, m2;
public Morph(Turtle m1, Turtle m2) {
this.m1 = m1;
this.m2 = m2;
}
インスタンスメソッドの定義
}
29
計算機プログラミングI (増原) 2003年度
Morph:インスタンスメソッド
• m.step() とすると、m1 と m2 の中間へ移動
public class Morph extends Turtle {
public Turtle m1, m2;
コンストラクタ(略)
}
public void step() {
自身をm1とm2の中間に移動
}
30
計算機プログラミングI (増原) 2003年度
Morphクラスの使用
Turtle mc = new Turtle();
Turtle ms = new Turtle();
Morph mm = new Morph(mc,ms);
for(i = 0; i < steps; i++){
mc (円)を移動
ms (星形)を移動
}
mm.step();
• 使う側は「どうやって中間の位置を
追いかけているか」を気にしなくてよい
31
計算機プログラミングI (増原) 2003年度
Morphクラスの使用
星
円
Turtle mc = new Turtle();
Turtle ms = new Turtle();
Morph m0 = new Morph(mc,ms);
Morph m1 = new Morph(mc,m0);
Morph m2 = new Morph(m0,ms);
for(i = 0; i < steps; i++){
mc (円)を移動
ms (星形)を移動
}
m0
m2
m1
m0.step(); m1.step(); m2.step();
• 「Morphオブジェクトを追いかけるMorphオブジェクト」
も可能 ← MorphはTurtleの子クラス
32
計算機プログラミングI (増原) 2003年度
Morphingの発展
• 各タートルはstepメソッドを呼び出されると、
各自、自分のやり方で適当な図を描くとする(T61.java)
• Morphingクラスもstepメソッドを呼び出されると、
適切な場所へ移動する
• 組み合わせれば
– stepによって星を描くタートルがあれば円と星のMorphing
– 手書きの図を覚えるタートルを作り、stepメソッドで再生するように
しておけば、手書き図のMorphing
– フラクタル図形をstepごとに描くタートルがあればフラクタル図形
のMorphing
• 必要なもの? ―― 共通の親クラス
33
計算機プログラミングI (増原) 2003年度