• 検索結果がありません。

レコード class Point attr_accessor("x", "y") インスタンス変数の宣言 point.rb

N/A
N/A
Protected

Academic year: 2021

シェア "レコード class Point attr_accessor("x", "y") インスタンス変数の宣言 point.rb"

Copied!
39
0
0

読み込み中.... (全文を見る)

全文

(1)
(2)

レコード

class Point attr_accessor("x", "y") end … point.rb インスタンス変数の宣言

(3)

irb(main):004:0> load ("point .rb") => true irb(main):005:0> p = Point.new() => #<Point:0x40332080> irb(main):006:0> p.x = 3 => 3 irb(main):007:0> p.y = 4 => 7 irb(main):008:0> p => #<Point:0 x40332080 @y=4, @x=3> オブジェクト (Pointクラスの インスタンス) インスタンス変数への代入 インスタンス変数への代入

(4)

irb(main):013:0> p.y => 4 irb(main):014:0> sqrt(p.x**2 + p.y**2) => 5.0 (もちろん include(Math) が必要…)

•  実はRubyでは,レコードもオブジェクトの一種

(オブジェクトについては後で説明する)

インスタンス変数の参照

(5)

… def point_make(u,v) p = Point.new() p.x = u p.y = v p end … レコード/オブジェクト(への参照)を返す point.rb

(6)

… def point_scale(p,s) point_make(p.x*s, p.y*s) end def point_add(p,q) point_make(p.x+q.x, p.y+q.y) end … point.rb 点の座標値のスカラ倍 点の座標値の加算

(7)

def point_interpolate(p,q,t)

point_add(point_scale(p,1-t), point_scale(q,t)) end

def point_draw(p,a)

if 0 <= p.y+0.5 && p.y+0.5 < a.length() && 0 <= p.x+0.5 && p.x+0.5 < a[0].length() a[p.y+0.5][p.x+0.5]=1 end end point.rb 配列aの縦の長さ 配列aの横の長さ Rubyの配列の添え字が実数でも いいことを利用している 0.5は四捨五入をするため

(8)

load("./max.rb") load("./abs.rb")

def line_draw(p0,p1,a)

n=max(abs(p1.x - p0.x), abs(p1.y - p0.y)) for i in 0..n point_draw(point_interpolate(p0,p1,i*1.0/n), a) end end line.rb 2点間の差分ベクトルの x,yの大きい数

(9)

def bezier_draw(p0,c,p1,a) n = 10 prev = p0 for i in 1..n t = i*1.0/n q0 = point_interpolate(p0, c, t) q1 = point_interpolate(c, p1, t) r = point_interpolate(q0, q1, t) line_draw(prev, r, a) prev = r end end bezier.rb 曲線をn等分する prev から r まで線分を描く

(10)

練習

•  授業のWebページから point.rb,line.rb,

bezier.rb, kana.rb をダウンロードして、練習

8.1 a) の図形 kana() を描画してみなさい。

abs.rb, max.rb, make1d.rb, make2d.rb などは、 すでに作成したものである)

(11)

進捗状況の確認

1.  kana()が表示できた(できた時点で投票して

ください)

できた人は、教科書168ページの練習 8.2 に

挑戦してみてください。

(12)

Line や Bezier もレコードにできる

class Line attr_accessor("p0", "p1") end class Bezier attr_accessor("p0", "c", "p1") end

(13)

load("./max.rb") load("./abs.rb")

def line_draw(l, a)

n=max(abs(l.p1.x – l.p0.x), abs(l.p1.y – l.p0.y)) for i in 0..n point_draw(point_interpolate(l.p0,l.p1,i*1.0/n), a) end end line.rb 2点間の差分ベクトルの x,yの大きい数

(14)

レコードの意義

•  一まとまりの複数のデータを表す変数が1個で

すむ

– Pointの場合,x座標とy座標をまとめて扱える – Lineの場合,始点と終点をまとめて扱える •  4つの座標をまとめて扱えている – Bezierの場合,始点と終点,第3点をまとめて扱える •  6つの座標をまとめて扱えている

•  このようなデータのまとまりを値として扱える

– 関数の値として返すことができる •  4つの座標を一度に返すことは通常の関数では難しい

(15)

しかし,レコードのままだと

•  クラス(レコードの種類)ごとに操作を定義

Pointクラス Lineクラス Bezierクラス point_draw line_draw bezier_draw

•  これらが混在している場合,

if figureがPointクラスのオブジェクト point_draw(figure,a) else if figureがLineクラスのオブジェクト line_draw(figure,a) else if figureがBezierクラスのオブジェクト bezier_draw(figure,a)

(16)

オブジェクト指向では

•  オブジェクト(ここではレコードのこと)に自分に

対する操作の仕方を覚えさせる

– Pointのレコードに,スカラー倍,ベクトル和などの 操作を覚えさせる – クラス定義の中で操作法(メソッド)を定義する

•  「カプセル化(encapsulation)」

•  「継承(inheritance)」

•  「多相性(polymorphism)」

(17)

class Point attr_accessor("x", "y") def initialize(u,v) self.x = u self.y = v end def scale(s) Point.new(self.x * s, self.y * s) end def add(q)

Point.new(self.x + q.x, self.y + q.y) end end

クラス

scaleメソッド 初期化メソッド 自分自身の x @x でもよい oo-point.rb 自分の操作法を クラス定義の一部とし メソッドと呼ぶ ↓ 「カプセル化」

(18)

irb(main):005:0> p = Point .new(3,4) => #<Point:0x7ffa3ed8 @x=3, @y=4> irb(main):006:0> p.x => 3 irb(main):007:0> q = p.scale(2) => #<Point:0x7ffa3e84 @x=6, @y=8> irb(main):008:0> p.add(q) => #<Point:0x7ff9b0f8 @x=9, @y=12> irb(main):009:0> p.add(q).scale(0.5) => #<Point:0x7ff94ca8 @x=4.5, @y=6.0> 初期化メソッドの引数 新しいオブジェクト 新しいオブジェクト 新しい オブジェクト

(19)

load("./max.rb") load("./abs.rb")

load("./oo-point.rb”)

def line_draw(p0, p1, a)

n=max(abs(p1.x – p0.x), abs(p1.y – p0.y)) for i in 0..n p0.interpolate(p1,i*1.0/n).draw(a) end end line_draw.rb 2点間の差分ベクトルの x,yの大きい数

(20)

isrb(main):001:0> load("line_draw.rb") true isrb(main):002:0> load("make2d.rb") true isrb(main):003:0> a=make2d(200,200) : (省略) isrb(main):004:0> show(a) : (省略) isrb(main):005:0> line_draw(Point.new(50,50),Point.new(100,170),a) 0..120 isrb(main):006:0> line_draw(Point.new(40,150),Point.new(180,70),a) 0..140

(21)

練習

•  授業のページから oo-point.rb をダウンロー

ドして、練習

8.5 b), c) を追加したら、

line_draw.rb で直線を描画してみなさい。

進捗状況の確認

1.  直線が描画できた時点で投票してください。

 その後、練習 8.5 a), d) も定義してください。

(22)

進捗状況の確認

1.  直線が描画できた時点で投票してください。

2.  drawメソッドはできた。

3.  interpolateメソッドはできた。

4.  どちらもできていない。

(23)

load("oo-point.rb") load("line_draw.rb") class Figure def draw(a) pnts = self.points() for i in 0..(pnts.length()-2) line_draw(pnts[i], pnts[i+1], a) end end end oo-figure.rb drawメソッドの定義 Figureの種類によらず pointsメソッドで 点列が得られる ↓ 「カプセル化」 「多相性」

(抽象的な) クラス Figure

(24)

load("oo-figure.rb") class Line < Figure

attr_accessor("p0", "p1") def initialize(q,r) self.p0 = q self.p1 = r end def points() [self.p0, self.p1] end end oo-line.rb Figureオブジェクトの継承 drawメソッドを使える ↓ 「継承」 pointsメソッドは 両端点を返す ↓ 「カプセル化」

(25)

load("oo-line.rb") class Bezier < Line

attr_accessor("p0", "c", "p1") def initialize(q,r,s) super(q,s) self.c = r end def points() n = 16 pnts = Array.new(n+1) pnts[0] = self.p0 pnts[n] = self.p1 for i in 1..(n-1) t = i*1.0/n q0 = self.p0.interpolate(c, t) q1 = self.c. interpolate(p1, t) pnts[i] = q0.interpolate(q1, t) end pnts end end oo-bezier.rb Lineオブジェクトの継承 drawメソッド (iniAalizeメソッド) pointsメソッドは t=0〜1の点列 (16+1個) ↓ 「カプセル化」 最初と最後の点は self.p0 と self.p1 線形補間を2段階して 曲線上の点を求める 点列を返す LineクラスのiniAalizeメソッドを呼び出す

(26)

練習

•  授業のページから oo-figure.rb, oo-line.rb,

oo-bezier.rb, drawall.rb をダウンロードして、

drawline() と drawground() を実行してみな

さい。

進捗状況の確認

1.  drawground() ができたら投票してください。

  その後は、oo-circle.rbを完成させて、

drawmoon() を実行してください。ただし、練

習 8.5 d) の rotate メソッドが必要です。

(27)

回転

(rotate)と円の描画

•  点の回転 (θ)

•  円の描画(正多角形)

x 軸上の点 original を θ 回転してから平行移動 ! x ! y " # $ % &

' ="#$ cossinθθ −sincosθθ %&'" xy # $ % & ' x y self.c original pnts[0]=pnts[1] θ pnts[1] self.r

(28)

load("oo-figure.rb") include(Math)

class Circle < Figure attr_accessor("c", ”r") def initialize(q,r) self.c = q self.r = r end def points() n = 32 pnts = Array.new(n+1) original = Point.new(self.r, 0) pnts[0] = original.add(self.c) pnts[n] = pnts[0] for i in 1..(n-1) pnts[i] = # 自分で書く end pnts end end oo-circle.rb Figureオブジェクトの継承 drawメソッドを使える pointsメソッドは t=0〜1の点列 (32+1個) ↓ 「カプセル化」 originalを平行移動する originalの回転と平行移動 で円周上の点列を作る 点列を返す 原点を中心とした円の 始点=終点(original)を作る

(29)

多相性の実現

def drawall(elements,a)

for i in 0..elements.length()-1

elements[i].draw(a)

end

end

elements[i] のクラスが何であれ, 共通したdrawメソッドが使える ↓ 「多相性」 drawall.rb

(30)

… def drawmoon() p0=Point.new(0,85) p2=Point.new(99,85) f=[Line.new(p0,p2), Bezier.new(p0,Point.new(50,60),p2), Circle.new(Point.new(66,20),20)] a=make2d(100,100) drawall(f,a) show(a) end drawall.rb

(31)

def drawall(elements,a)

for i in 0..elements.length()-1

figure_draw(elements[i], a)

end

end

def figure_draw(f,a)

pnts = f.points()

for i in 0..(pnts.length()-2)

line_draw(pnts[i], pnts[i+1], a)

end

end

f のクラスが何であれ, 共通したpointsメソッドが使える ↓ 「多相性」

多相性

drawall_fig.rb 必ずしもdrawメソッド でなくても構わない たとえば関数でも良いが

(32)

レコードのままでは

•  クラス(レコードの種類)ごとに操作を定義

Pointクラス Lineクラス Bezierクラス point_draw line_draw bezier_draw

•  これらが混在している場合,

if figureがPointクラスのオブジェクト point_draw(figure,a) else if figureがLineクラスのオブジェクト line_draw(figure,a) else if figureがBezierクラスのオブジェクト bezier_draw(figure,a)

(33)

カプセル化,継承,多相性

•  カプセル化 –  「もの」を表すデータとメソッドをまとめ,決められたインス タンスメソッドを通してのみインスタンス変数にアクセスを させ,インスタンス変数に直接触らせないこと •  継承 –  他のクラスのインスタンス変数やメソッドを受け継いで新 しいクラスを作ること新しいクラスをサブクラス,元のクラ スをスーパークラスと呼ぶ •  多相性 –  同じ名前のインスタンスメソッドを呼び出しても,適用され たオブジェクトによって異なるメソッドを呼び出すこと実行 時にメソッドを探す動的結合で実現する

(34)

ベクトルの計算など

•  ベクトルの演算 – 「カプセル化」

加減算、内積、正規化、直交ベクトルなど Pointクラス内のメソッド定義 … def dotProduct(v) self.x*v.x + self.y*v.y end def normalize() self.scale(1.0 / sqrt(self.dotProduct(self))) end def normal() Point.new(self.y, -self.x).normalize() end oo-point.rb ベクトルの操作法を クラス内で メソッドとして定義する ↓ 「カプセル化」

(35)

直線

(線分)の交点計算

•  交差の条件

線分の両端点が他方の直線の両側(左右)に分かれる = 符号付距離が異符号になる

•  交点の位置

線分の両端点を他方の直線からの距離で内分(線形補間)

(36)

def line_intersect(p0,p1,q0,q1) pNormal = p1.sub(p0).normal() qStart = pNormal.dotProduct(q0.sub(p0)) qEnd = pNormal.dotProduct(q1.sub(p0)) qNormal = pStart = pEnd =

if (qStart * qEnd > 0) || (pStart * pEnd > 0) nil

else

p0.interpolate(p1, ((pStart * 1.0) / (pStart - pEnd))) end

end

(37)

練習

•  line_intersect.rb の line_intersect メソッドを

定義しなさい。

•  授業のページから drawintersect.rb をダウン

ロードして,

drawintersect() を実行しなさい。

Circle クラスが必要)

進捗状況の確認

1.  drawintersect()が表示できた(できた時点で

投票してください)

(38)

: def intersect(other) spnts = self.points() opnts = other.points() results = Array.new() for i in 0..(spnts.length()-2) for j in 0..(opnts.length()-2) point = line_intersect(spnts[i],spnts[i+1],opnts[j],opnts[j+1]) if point != nil results[results.length()] = point end end end results end : oo-figure.rb Figureオブジェクト間の intersectメソッドの定義 線分列同士の交点 pointsメソッドで 点列が得られる 大きさ0の配列 (でも中に入る!) 交点があれば 配列の最後に加える (配列は自動的に 1つ大きくなる) 交点列(配列)を返す 隣接2点からなる線分 の間で交点を計算する

(39)

def drawintersect() a = make2d(400, 400) f = [Line.new(Point.new(50, 50), Point.new(300, 350)), Circle.new(Point.new(199, 199), 70), Bezier.new(Point.new(50, 200), Point.new(150, 50), Point.new(350, 300)), Bezier.new(Point.new(250, 50), Point.new(100, 150), Point.new(200, 350))] for i in 0..(f.length()-1) f[i].draw(a) for j in (i+1)..(f.length()-1) results = f[i].intersect(f[j]) for k in 0..(results.length()-1) Circle.new(results[k], 4).draw(a) end end end show(a) end drawintersect.rb i番目のFigureを描く i番目とj番目のFigureの 交点を配列で受け取る 各交点の位置に 半径4の小円を描く

参照

関連したドキュメント

重要な変調周波数バンド のみ通過させ認識性能を向 上させる方法として RASTA が知られている. RASTA では IIR フィルタを用いて約 1 〜 12 Hz

2000 個, 2500 個, 4000 個, 4653 個)つないだ 8 種類 の時間 Kripke 構造を用いて実験を行った.また,三つ

• 問題が解決しない場合は、アンテナレベルを確認し てください(14

 ESET PROTECT から iOS 端末にポリシーを配布しても Safari の Cookie の設定 を正しく変更できない現象について. 本製品で iOS

子どもたちは、全5回のプログラムで学習したこと を思い出しながら、 「昔の人は霧ヶ峰に何をしにきてい

さらに, 会計監査人が独立の立場を保持し, かつ, 適正な監査を実施してい るかを監視及び検証するとともに,

海なし県なので海の仕事についてよく知らなかったけど、この体験を通して海で楽しむ人のかげで、海を

彩度(P.100) 色の鮮やかさを 0 から 14 程度までの数値で表したもの。色味の