ScalaでProject Euler(54)

次問の準備でクラスの理解も兼ねて分数クラスを作ります。
まず、クラス名はPythonと同じくFractionとします。分母・分子は都合でBigIntです。

コンストラク

分母と分子を引数に取ります。こんな感じでしょうか。

class Fraction(num :BigInt, den :BigInt) {
    val d = gcd(num, den)
    private val numerator = num / d
    private val denominator = den / d
    
    def gcd(m :BigInt, n :BigInt) :BigInt =
        if(n == 0) m else gcd(n, m % n)
}

ただし、これではdもメンバになってしまいます。

println ((new Fraction(4, 6)).d)    // 2

ちょっと無理やりっぽいですが、これでいけるようです。

class Fraction(num :BigInt, den :BigInt) {
    private val (numerator, denominator) = init(num, den)
    
    def init(num :BigInt, den :BigInt) = {
        val d = gcd(num, den).abs
        if(den > 0)
            (num / d, den / d)
        else
            (-num / d, -den / d)
    }
    
    def gcd(m :BigInt, n :BigInt) :BigInt =
        if(n == 0) m else gcd(n, m % n)
}

toString

toStringをオーバーライドするとprintで表示できます。Javaと同じ?

class Fraction(num :BigInt, den :BigInt) {
    ...
    override def toString() =
        if(denominator != 1)
            numerator.toString + "/" + denominator.toString
        else
            numerator.toString
    ...
}

println (new Fraction(4, 6))    // 2/3

オーバーライドするときはoverrideと書かなければならないようです。基本クラスのメソッドをオーバーライドしているんですね。

演算子

Scalaでは演算子もメソッドなのでそのままですね。

class Fraction(num :BigInt, den :BigInt) {
    ...
    def *(f :Fraction) =
        new Fraction(numerator * f.numerator, denominator * f.denominator)
    ...
}

val f = new Fraction(4, 6)
val g = new Fraction(1, 6)
println (f * g)     // 1/9

整数との演算も簡単です。

    def *(n :BigInt) =
        new Fraction(numerator * n, denominator)
...
println (f * 3)     // 2

ただ、逆はできないですよね。

println (3 * f)     // エラー

Fractionクラスとしてはまだまだ足りないものだらけですが、次の問題を解くためにはこれで十分です。

class Fraction(num :BigInt, den :BigInt) {
    private val (numerator, denominator) = init(num, den)
    
    def init(num :BigInt, den :BigInt) = {
        val d = gcd(num, den).abs
        if(den > 0)
            (num / d, den / d)
        else
            (-num / d, -den / d)
    }
    
    def *(f :Fraction) =
        new Fraction(numerator * f.numerator, denominator * f.denominator)
    
    def *(n :BigInt) =
        new Fraction(numerator * n, denominator)
    
    def +(f :Fraction) =
        new Fraction(numerator * f.denominator + denominator * f.numerator,
                                                denominator * f.denominator)
    
    override def toString() =
        if(denominator != 1)
            numerator.toString + "/" + denominator.toString
        else
            numerator.toString
    
    def gcd(m :BigInt, n :BigInt) :BigInt =
        if(n == 0) m else gcd(n, m % n)
}