知ってました?
重要:これから書くコードはとてもメニアックなので参考にしないようにして下さい。
今日は有理数を定義してみます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
case class Rational(n: Int, d: Int) |
加算と減算を追加してみる。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
case class Rational(n: Int, d: Int) { | |
def +(r: Rational) = Rational(n * r.d + r.n * d, d * r.d) | |
def -(r: Rational) = Rational(n * r.d - r.n * d, d * r.d) | |
} |
さて、この2つのメソッド、とても似ていますよね。
抽象化してしまいます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
case class Rational(n: Int, d: Int) { | |
def op(f: (Int, Int) => Int)(r: Rational) = | |
Rational(f(n * r.d, r.n * d), d * r.d) | |
lazy val + = op(_ + _)_ | |
lazy val - = op(_ - _)_ | |
} |
さて、今日の本題はここからです。
この加算と減算を他の数値型に対応させたいとします。
普通はこのようにオーバーロードを使いますよね。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
case class Rational(n: Int, d: Int) { | |
def +(r: Rational) = Rational(n * r.d + r.n * d, d * r.d) | |
def +(i: Int) = Rational(n + i * d, i * d) | |
} |
しかし、+や-はメソッド型ではなく、値型なのでオーバーロードは使用できません。
そこで、オーバーロードを使わず、UnionTypesを使って定義します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
case class Rational(n: Int, d: Int) { | |
def +[A](a: A)(implicit ev: A ∈ t[Rational]#t[Int]) = | |
a match { | |
case Rational(nn: Int, dd: Int) => Rational(n * dd + nn * d, d * dd) | |
case i: Int => Rational(n + i * d, i * d) | |
} | |
} |
これでもまだ問題があります。
Scalaでは、型パラメータをとる関数を値として定義できません。
そこで、関数のようにふるまう型を自分で定義します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
case class Rational(n: Int, d: Int) { | |
case class Function(f: (Int, Int) => Int) { | |
def apply[A](a: A)(implicit ev: A ∈ t[Rational]#t[Int]) = a match { | |
case Rational(nn: Int, dd: Int) => Rational(f(n * dd, nn * d), d * dd) | |
case i: Int => Rational(f(n, i * d), i * d) | |
} | |
} | |
lazy val + = Function(_ + _) | |
lazy val - = Function(_ - _) | |
} |
これで+と-の演算を抽象化し、さらに多相に対応しましたね。
めでたしめでたし。
0 件のコメント:
コメントを投稿