본문 바로가기

Programming/Scala

Scala - Implement rational number (Scala 언어로 유리수 구현)

보통 Java 언어에서는 int type 의 변수와 유리수 type 의 변수를 계산하기 위해서는 lvalue 가 유리수 type 이 아니기 때문에, int type 의 변수를 유리수 type 의 wrapper 클래스를 이용하여, boxing 이라는 과정을 거치고 다시 오른쪽 변수인 유리수 type 의 변수를 인자를 갖는 method 를 호출하여 해결 한다.


예를 들어서, Java 언어로 구현된, 유리수 클래스가 있다고 하자.

Rational.java


public class Rational {

    public Rational add(Rational rvalue) { ... }

    ... // 내부 method 들 구현 생략

}


int a = 10;

Ratioan r = new Rational (1, 3) // 분수 1/3 을 의미


Rational result = a.add(r); // 이 문법은 허용이 되지 않는다.

Rational ra = Rational(a); // 정수인 a 를 유리수를 대변하는 클래스의 객체로 형 변환을 한다.

Rational result2 = ra.add(r); // 이렇게 계산을 해야 문제가 없다.


위와 같은 코드는 자연스럽지 못하다. 왜냐하면 기본적으로 Java 언어에서는 Operator Overloading 을 허용하지 않기 때문이다. add 대신에 + 를 사용하면 훨씬 자연스러워 보이지 않을까?


하지만, Scala 는 이런 문제를 해결하기 위해서 좀 더 유연한 방법을 제시한다. Scala 언어에서는 Operator Overloading 을 허용한다. 그리고 묵시적 형변환이라는 기능도 제공한다. 무슨 말인지 자세히 알아보기 위해서, 아래의 코드를 살펴보자. 아래의 코드는 유리수를 나타내는 클래스를 Scala 언어로 구현한 것이다.


/** 

유리수를 나타내는 클래스 구현

유리수의 덧셈(+), 뺄셈(-), 곱셈(*),나눗셈(/) 이 구현되어 있다.

유리수의 공통분모를 구하기 위해서 GCD(Great Common Divisor) 최대공약수를 Recursive 함수로 구현하였다.

계산 가능한 수식은 유리수 연산자 유리수, 유리수 연산자 정수 의 형태만 가능하다.

TODO 유리수 연산자 실수 를 지원

*/


// Companion class of Rational singleton object

class Rational(n: Int, d: Int) {

require(d != 0)


private val g = gcd(n.abs, d.abs)

val numer: Int = n / g

val denom: Int = d / g

def this(n: Int) = this(n, 1)


def + (that: Rational): Rational = {

new Rational(numer * that.denom + that.numer * denom, denom * that.denom)

}

def + (that: Int)Rational = {

new Rational(numer + denom * that, denom)

}


def - (that: Rational): Rational = {

new Rational(numer * that.denom - that.numer * denom, denom * that.denom)

}

def - (that: Int): Rational = {

new Rational(numer - denom * that, denom)

}


def * (that: Rational): Rational = new Rational(numer * that.numer, denom * that.denom)

def * (that: Int): Rational = new Rational(numer * that, denom)


def / (that: Rational): Rational = new Rational(numer * that.denom, denom * that.numer)

def / (i: Int): Rational = new Rational(numer, denom * i)


override def toString = numer + "/" + denom


private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b)


}


// Copanion object of Rational class

object Rational {

/**

Int 클래스에 존재하는 연산자 method 는 rvalue 로 Rational 클래스를 허용하지 않기 때문에,

Int type 의 변수와 Rational type 의 변수 연산을 하기 위해서는 이 method 가 필요하다.

이 Method 는 Int type 의 변수를 필요할 때, Rational type 으로 형 변환을 해준다.

*/

implicit def intToRational(x: Int) = new Rational(x)


def main(args: Array[String]) {

val x = new Rational(16, 42)

val x2 = new Rational(50, 42)


println("16/42 = 8 / 21")

println("50/42 = 25 / 21")


print("\n8/21 + 25/21 = ")

println(x + x2)


print("\n8/21 + 1 = ")

println(x + 1)


print("\n8/21 - 25/21 = ")

println(x - x2)


print("\n8/21 - 2 = ")

println(x - 2)



print("\n8/21 * 25/21 = ")

println(x * x2)



print("\n2 * 8/21 = ")

println((2 * x))


print("\n8/21 / 25/21 = ")

println(x / x2)



print("\n2 / 8/21 = ")

println((2 / x))


println("2 + 4 = " + (2 + 4))

}

}