Оператор перегрузки Kotlin - PullRequest
0 голосов
/ 12 февраля 2019

Я новичок в kotlin и работаю над перегрузкой операторов для пользовательского класса, который я определил.Класс называется «Rational» и представляет собой рациональное число, например, 117/1098.Класс определен, как показано ниже, и я перегружен кучей операторов, таких как плюс, минус, раз и так далее.Однако я не уверен, что мне нужно сделать, чтобы перегрузить оператор «in».

Вот мой класс:

data class Rational(val rational: String) {
    private val numerator: BigInteger
    private val denominator: BigInteger

    init {
        val splitted = rational.split("/")
        numerator = splitted[0].toBigInteger()
        denominator = when (splitted[1]) {
            "0" -> throw Exception("not allowed")
            else -> splitted[1].toBigInteger()
        }
    }

    operator fun plus(number: Rational): Rational {
        val gcm = denominator * number.denominator
        val numerator = (gcm / denominator) * numerator + (gcm / number.denominator) * number.numerator
        return Rational("$numerator/$gcm")
    }

    operator fun minus(number: Rational): Rational {
        val gcm = denominator * number.denominator
        val numerator = (gcm / denominator) * numerator - (gcm / number.denominator) * number.numerator
        return Rational("$numerator/$gcm")
    }

    operator fun times(number: Rational): Rational {
        val numerator = numerator * number.numerator
        val denominator = denominator * number.denominator
        return Rational("$numerator/$denominator")
    }

    operator fun div(number: Rational): Rational {
        val numerator = numerator * number.denominator
        val denominator = denominator * number.numerator
        return Rational("$numerator/$denominator")
    }

    operator fun compareTo(number: Rational): Int {
        val ratio = this.numerator.toFloat() / this.denominator.toFloat()
        val numberRatio = number.numerator.toFloat() / number.denominator.toFloat()
        if (ratio > numberRatio) {
            return 1
        } else if (ratio == numberRatio) {
            return 0
        }
        return -1
    }

    operator fun unaryMinus(): Rational {
        val inverseNumerator = -numerator
        return Rational("$inverseNumerator/$denominator")
    }

    operator fun unaryPlus(): Rational {
        return Rational("$numerator/$denominator")
    }

    operator fun rangeTo(end: Rational): Any {
        var range: MutableList<Rational> = arrayListOf()
        val startNumerator = this.numerator.toInt()
        val endNumerator = end.numerator.toInt()
        var index = 0
        if (this.denominator == end.denominator) {
            for (i in startNumerator..endNumerator) {
                range.add(index, Rational("$i/$denominator"))
            }
        }
        return range
    }

    operator fun contains(number: Rational): Boolean {
        if (this.denominator % number.denominator == 0.toBigInteger()
                && this.numerator <= number.numerator) {
            return true
        }
        return false
    }

    override fun toString(): String {
        val gcd = numerator.gcd(denominator)
        return if (gcd != null) {
            val newNumerator = numerator / gcd
            val newDenominator = denominator / gcd
            "$newNumerator/$newDenominator"
        } else {
            "$numerator/$denominator"
        }
    }
}
infix fun Int.divBy(denominator: Int): Rational {
    if (denominator == 0) {
        throw Exception("denominator 0 not allowed")
    }

    return Rational("$this/$denominator")
}

infix fun Long.divBy(denominator: Long): Rational {
    if (denominator == 0L) {
        throw Exception("denominator 0 not allowed")
    }
    return Rational("$this/$denominator")
}

infix fun BigInteger.divBy(denominator: BigInteger): Rational {
    if (denominator == 0.toBigInteger()) {
        throw Exception("denominator 0 not allowed")
    }
    return Rational("$this/$denominator")
}

fun String.toRational(): Rational {
    return Rational(this)
}

А вот мой основной корпус, который, очевидно, все ещене компилируется:

fun main() {
    val half = 1 divBy 2
    val third = 1 divBy 3
    val twoThirds = 2 divBy 3

    println(half in third..twoThirds) // this line does not compile beacause in operator is not defined for the class
}

Наверное, мне нужно переопределить оператор rangeTo, но я не уверен насчет прототипа оператора.Есть ли кто-нибудь, кто может помочь мне выбрать правильный путь?

Ответы [ 2 ]

0 голосов
/ 12 февраля 2019

Чтобы заставить in работать, вызов third..twoThirds возвращает что-то , которое имеет метод contains(Rational), то есть то, на что вызов in переводится.

Один из способов сделать это - вернуть ClosedRange<Rational> здесь, вот так:

operator fun rangeTo(end: Rational): ClosedRange<Rational> {
    return object : ClosedRange<Rational> {
        override val endInclusive: Rational = end
        override val start: Rational = this@Rational
    }
}

Это накладывает ограничение типа на Rational, так как ClosedRange необходимоComparable реализация, чтобы иметь возможность определить, принадлежит ли значение в нем.Это можно сделать, внедрив интерфейс Comparable, а затем добавив operator к существующему оператору compareTo (плюс рекомендуется переименовать параметр в соответствии с интерфейсом):

data class Rational(val rational: String) : Comparable<Rational> {

    ...

    override operator fun compareTo(other: Rational): Int {
        val ratio = this.numerator.toFloat() / this.denominator.toFloat()
        val numberRatio = other.numerator.toFloat() / other.denominator.toFloat()
        if (ratio > numberRatio) {
            return 1
        } else if (ratio == numberRatio) {
            return 0
        }
        return -1
    }

}

Вы также можете полностью избежать преобразования в числа с плавающей точкой, используя вместо этого эту реализацию, как предложено в комментарии ниже @gidds:

override operator fun compareTo(other: Rational): Int {
    return (numerator * other.denominator - denominator * other.numerator).signum()
}

Кроме того, ваша текущая реализация contains может вероятно будет отброшено, так как оно вам больше не нужно, и оно функционирует довольно странно.


Чтобы добавить что-то кроме прямого ответа здесь: как предложил Евгений Петренко в своем ответе,было бы целесообразно добавить пару конструкторов, отличных от того, который использует String, например, тот, который занимает два Int с, и один, который занимает два BigIntegers с.

0 голосов
/ 12 февраля 2019

Оператор in объявлен обратным.Вам нужна функция расширения с правой стороны, которая занимает левую сторону.

https://kotlinlang.org/docs/reference/operator-overloading.html#in

Вам не хватает инфиксной функции divBy, позволяющей тьюринг Int в Rational, например

infix fun Int.divBy(i: Int) = Rational("$this/$i")

Не будет работать такой код, как val half = 1 divBy 2.Теоретически, имеет смысл добавить конструктор для Rational из Int s, чтобы избежать синтаксического анализа.

В методе rangeTo в классе Rational имеется неверный тип возврата, он не долженбыть Any.Он должен быть объявлен как

data class RationalRange(val left: Rational, val right: Rational) {
  operator fun contains(r: Rational) = left <= r && r <= right
}

operator fun rangeTo(end: Rational): RationalRange(this, end) 

Теперь пример с x in a..b должен работать.

UPD: добавлен RationalRange.Я упустил момент, извини.Вам вообще не нужна функция contains, реализованная для класса Rational.

Функция compareTo в Rational вряд ли будет использовать .toFloat() вместо этого, вы можете реализовать это непосредственно с целыми числами

...