Top Banner
Scala でプログラム 作ってみたので Scala 作法を教えてくださ
32

Rpscala2011 0601

Jul 11, 2015

Download

Hajime Yanagawa
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Rpscala2011 0601

Scalaでプログラム作ってみたので

Scalaの作法を教えてくださ

Page 2: Rpscala2011 0601

自己紹介 (株 ) オープンストリーム 所属 twitter yangiYa COBOL → Java (→ Scalaになれたら。。 )

会社ではあまりプログラム作る機会がなくなってきた。欲求不満

Scalaは去年の秋ぐらいからはじめた Scalaの書籍で読んだもの

Scalaプログラミング入門 デイビッド・ポラック (日経 BP社 )

プログラミング Scala Dean Wamloer/Alex Payne (オライリー )

Page 3: Rpscala2011 0601

作ってみたものの説明

Page 4: Rpscala2011 0601

行列モデルを Scalaで実装

Page 5: Rpscala2011 0601

利用用途行列で表現できる概念に汎用的に利用

「物体」 (ゲームのキャラクタなど )の座標

「物体」の速度 「物体」の大きさ 将棋、囲碁、チェス、オセロ、などの駒の位置

Page 6: Rpscala2011 0601

要求仕様(モデルの値)1.座標は、x,y,z• x,y,z のデフォルトは Double• 平面上のオブジェクト表現の場合は、 x,yだけで管理したい

• x,y,z を整数で管理したい場合もある。(整数同士の計算結果が少数点以下の数字にならないようにしたい)

Page 7: Rpscala2011 0601

要求仕様(モデルのサービス)1.行列の加算( 2,1 ) + (3,1) = (5,1)

Page 8: Rpscala2011 0601

要求仕様(モデルのサービス)• 行列の k倍( 2,1 ) × 3 = (6,3 )

Page 9: Rpscala2011 0601

インタフェース設計trait Matrix {

/**[[scala.Int]]型の x 座標 */

def xByInt: Int

/**[[scala.Int]]型の y 座標 */

def yByInt: Int

/**[[scala.Int]]型の z 座標 */

def zByInt: Int

/**x 座標 */

def x: Double

/**y 座標 */

def y: Double

/**z 座標 */

def z: Double

}

Page 10: Rpscala2011 0601

インタフェース設計 /** この行列と引数の行列の和を返却する

*/

def add(matrix: Matrix): Matrix

def +(matrix: Matrix): Matrix = add(matrix)

Page 11: Rpscala2011 0601

インタフェース設計 /** この行列の実数倍 (引数 ) を返却する

*/

def product(scalarValue: Long): Matrix

/** この行列の実数倍 (引数 ) を返却する*/

def product(scalarValue: Int): Matrix

/** この行列の実数倍 (引数 ) を返却する*/

def product(scalarValue: Double): Matrix

Page 12: Rpscala2011 0601

インタフェース設計 /** この行列に実数倍 (引数 )した行列と、この

行列との和を返却する */ def productValueAndAdd(scalarValue: Long):

Matrix

/** この行列に実数倍 (引数 )した行列と、この 行列との和を返却する */

def productValueAndAdd(scalarValue: Int): Matrix

/** この行列に実数倍 (引数 )した行列と、この 行列との和を返却する */

def productValueAndAdd(scalarValue: Double): Matrix

Page 13: Rpscala2011 0601

要求仕様(非機能)• 低いオブジェクト生成コスト頻繁に利用されるバリューオブジェクトを想定

• スレッドセーフ複数のスレッドから利用されることを想定

3.利用者インタフェースのみに縛られる1. 利用者は具象クラスや内部実装に縛られたくない

2. インタフェースを守る範囲で内部実装は自由に変えたい

Page 14: Rpscala2011 0601

非機能要求からの方針1. オブジェクト生成コストは小さいほうがいい

– オブジェクトが使うメモリは小さく– オブジェクト生成時の処理ロジックは少なく

• イミュータブル– 複数スレッドからも安全に使用できる

• インターフェースを維持し、かつ、利用局面に応じた具象クラスを使えるようにする

• オブジェクト生成は、 javaの staticファクトリーメソッドのようなイメージで

• (Javaの例 ) java.util.Calendar#getInstance()==>Scala では、 object の        apply メソッドがよさそう

Page 15: Rpscala2011 0601

モデル設計 (一部抜粋 )

MatrixWithDouble

x:Doubley:Doublez:Double

MatrixWithInt

xByInt:IntyByInt:IntzByInt:Int

MatrixWithInt2D

xByInt:IntyByInt:Int

zByInt:Int = 0

MatrixWithDouble2D

x:Doubley:Double

z:Double = 0.0

Matrix

x:Doubley:Doublez:DoublexByInt:IntyByInt:IntzByInt:Int

<<インタフェース >>

Page 16: Rpscala2011 0601

オブジェクト生成object Matrix { //・・・コンパニオンオブジェクトと呼ばれる?

def apply(x: Int, y: Int, z: Int): MatrixWithInt = MatrixWithInt(x, y, z)

def apply(x: Int, y: Int): MatrixWithInt2D = MatrixWithInt2D(x, y)

def apply(x:Double,y:Double,z:Double): MatrixWithDouble =                         

MatrixWithDouble(x, y, z)

def apply(x: Double, y: Double): MatrixWithDouble2D =                         

MatrixWithDouble2D(x, y) }

Page 17: Rpscala2011 0601

機能仕様(=テスト仕様)抜粋assert(Matrix(1, 2, 3) === MatrixWithInt(1, 2, 3))

val mtx10_20_30Double = MatrixWithDouble(1.0, 2.0, 3.0)

assert(Matrix(1.0, 2.0, 3.0) === mtx10_20_30Double)

assert(Matrix(1.0, 2, 3) === mtx10_20_30Double)

assert(Matrix(1, 2) === MatrixWithInt2D(1, 2))

assert(Matrix(1.1, 2.2) === MatrixWithDouble2D(1.1, 2.2))

Page 18: Rpscala2011 0601

機能仕様(=テスト仕様)抜粋val mtx21_32_0 = MatrixWithInt2D(21, 32)

assert((Matrix(1, 2) + Matrix(20, 30)) === mtx21_32_0)

assert(Matrix(20, 30) + (Matrix(1, 2)) === mtx21_32_0)

val mtx111_222_333 = MatrixWithDouble(11.1, 22.2, 33.3)

assert(Matrix(10, 20) + Matrix(1.1, 2.2, 33.3) ===

                       mtx111_222_333)

assert(Matrix(1.1, 2.2, 33.3).add(Matrix(10.0, 20.0)) ===

                          mtx111_222_333)

Page 19: Rpscala2011 0601

機能仕様(=テスト仕様)抜粋

var m1: Matrix = Matrix(1, 2)

var m2: Matrix = Matrix(10, 20)

assert(m1 + m2 === MatrixWithInt2D(11, 22))

Page 20: Rpscala2011 0601

実装:オーバーロードとオーバーライドsealed trait Matrix {

/** この行列と引数の行列の和を返却する */

def add(matrix: Matrix): Matrix = Matrix(x + matrix.x, y + matrix.y, z + matrix.z)

}

private[core] abstract trait MatrixWithIntBase extends Matrix {

def add(point: MatrixWithInt): MatrixWithInt = MatrixWithInt(xByInt + point.xByInt, yByInt + point.yByInt, zByInt + point.zByInt)

def add(point: MatrixWithIntBase): MatrixWithIntBase = MatrixWithInt(xByInt + point.xByInt, yByInt + point.yByInt, zByInt + point.zByInt)

}

Page 21: Rpscala2011 0601

実装:オーバーロードとオーバーライドfinal case class MatrixWithInt2D private[core](xByInt: Int, yByInt: Int)

extends MatrixWithIntBase with Matrix2D {

def add(matrix: MatrixWithInt2D): MatrixWithInt2D = MatrixWithInt2D(xByInt + matrix.xByInt, yByInt + matrix.yByInt)

def +(matrix: MatrixWithInt2D): MatrixWithInt2D = add(matrix)

override def add(matrix: MatrixWithIntBase): MatrixWithIntBase = matrix match {

case pointWithInt2D: MatrixWithInt2D => add(pointWithInt2D) case pointWithInt: MatrixWithInt => add(pointWithInt) case other => super.add(other) }}

Page 22: Rpscala2011 0601

機能と実装の説明おわり

Page 23: Rpscala2011 0601

もっとやるとしたら (今回やってません)

1.機能要求1.行列の回転

2.非機能要求– Flyweight パターン2.オブジェクトキャッシュ

Page 24: Rpscala2011 0601

感想と

知りたいこと

Page 25: Rpscala2011 0601

感想 - Scaladoc

classと objectが横並び

コンストラクタを privateにしたら表示されない

リンクはもっと柔軟にかけたらいいのに

Page 26: Rpscala2011 0601

Scaladoc リンクはもっと柔軟にかけたらいいのに

1.[[クラス名フル修飾 ]] でリンクになる–パッケージ名を書くの面倒。

importされているのは、シンプルなクラス名だったらいいのに

• メソッドへのリンクつくれないの?なんか方法ないですか。おしえてください

3.Scaladoc文法を書いてあったページがなくなっているような。。。(Scaladoc 2 Documentation for Authors)

Page 27: Rpscala2011 0601

Scaladoc コンストラクタを privateにしたら表示されない

2.private コンストラクタが表示されないことはうれしい

• 利用者に、どうやってインスタンス化するかは、ドキュメントに書くしかないか?–誰もが迷うことなくインスタンス化する方法を把握できればいいのに。いい方法ないでしょうか。

Page 28: Rpscala2011 0601

Scaladoc classと objectが横並び

1.コンパイルした結果の1."class Hoge"と "object Hoge"は型の関係はなにもないはず

2.コンパイル後のクラスファイル名ってホントはちがう

1.Hoge、 Hoge$ のようなかんじ。。。

–設計上、暗黙の守るべきルールがあるのかではなかろうか?教えてください

Page 29: Rpscala2011 0601

object と applyメソッド今回作ったものの中では。。。                            コード再掲

sealed trait Matrix

object Matrix { def apply(x: Int, y: Int, z: Int): MatrixWithInt = MatrixWithInt(x, y, z) def apply(x: Int, y: Int): MatrixWithInt2D = MatrixWithInt2D(x, y) def apply(x: Double, y: Double, z: Double): MatrixWithDouble =

MatrixWithDouble(x, y, z) def apply(x: Double, y: Double): MatrixWithDouble2D =

MatrixWithDouble2D(x, y)}

ファクトリ役として使った

Page 30: Rpscala2011 0601

object と applyメソッド1. コンストラクタより、 applyメソッドのほうが、柔軟にインスタンス生成できる1. 生成する型を柔軟に切り替えられる2.シングルトンを返却したり、こっそりインスタンスをキャッシュすることもできる

3.利用者のコードを変更せず、内部実装を切り替えられる

1. こっそり、非パブリックなインスタンスを返却する用変更なんてことも可能

4.使用者が意図的に生成するインスタンスの型を決めたいなら、applyメソッドで作らず、 "createHoge"みたいなメソッドのほうがいい

5.コンストラクタより劣ること1.めんどくさい

Page 31: Rpscala2011 0601

object と applyメソッド1.コンパニオンオブジェクト

– 使命はなに?おしえてください

1.classと同名の objectをこうよぶのか?2.case class Foo() 、 object Foo

val foo :Foo = Foo後ろの Foo はオブジェクトなのかクラスなのかややこしい

• classと objectが関係あるよとの意思表示としてobject Foo extends Foo とかしたほうがいいとかあるのかな。。

Page 32: Rpscala2011 0601

おわり

ありがとうございました