Сократимость
Обыкновенную дробь называют несократимой, если ее числитель и знаменатель являются взаимнопростыми.
Наибольший общий делитель
Для определения взаимной простоты двух целых чисел, необходимо знать их наибольший общий делитель.
def gcd(x: Int, y: Int): Int =
if (b == 0) a
else if (a < 0) gcd(-a, b)
else if (b < 0) gcd(a, -b)
else if (b > a) gcd(b, a)
else gcd(b, a % b)
Метод gcd
(сокр. greatest common divisor — наибольший общий делитель) — это рекурсивная реализация алгоритма Евклида. Внимательно изучите его. Убедитесь, что полностью понимаете, как он работает, прежде чем следовать дальше.
Проверка на сократимость
Теперь добавим метод isReduced
, показывающий несократимость дроби.
def isReduced = gcd(numerator, denominator) == 1
Как Вы уже догадались, метод isReduced
возвращает логическое значение типа Boolean
.
Сокращение дроби
Класс должен поддерживать вычисление несократимой дроби, равной заданной.
Добавим метод reduced
, возвращающий несократимую дробь, равную заданной.
def reduced: Fraction =
if (isReduced) this
else new Fraction(
numerator / gcd(numerator, denominator),
denominator / gcd(numerator, denominator)
)
Как видно из реализации метода reduced
, дробь сперва проверяется на сократимость. Если дробь уже является несократимой, то возвращается текущий экземпляр, в противном случае вычисляются числитель и знаменатель новой дроби.
Рефакторинг
Рефакторинг — это эквивалентные преобразования кода, сохраняющее общее поведение, но существенно увеличивающие его качества.
Обратите внимание на методы isReduced
и reduced
. Легко заметить, что вызов gcd(numerator, denominator)
повторяется трижды. Выделим этот вызов в отдельный метод (такая операция рефакторинга называется extract method):
def gcd: Int = gcd(numerator, denominator)
Методы isReduced
и reduced
теперь можно упросить:
def isReduced = gcd == 1
def reduced: Fraction =
if (isReduced) this
else new Fraction(numerator / gcd, denominator / gcd)
При вызове reduced
одно и то же значение наибольшего общего делителя может быть вычислено целых три раза. Поскольку поля numerator
и denominator
неизменяемы, мы легко можем заменить метод gcd
на поле.
val gcd: Int = gcd(numerator, denominator)
Теперь наибольший общий делитель будет вычислен только один раз — в момент инициализации экземпляра. Отсутствие необходимости вычислений при каждом вызове методов reduced
и isReduced
существенно повышает их производительность.
Подробнее о взаимозаменяемости полей и методов Вы узнаете при обсуждении принципов единообразного доступа.
Проверка
val f1 = new Fraction(8,-6)
f1: Fraction = -8/6
val f2 = f1.reduced
f2: Fraction = -4/3
f1.isReduced
res0: Boolean = false
f2.isReduced
res0: Boolean = true