代码之家  ›  专栏  ›  技术社区  ›  Nicola Gallazzi

运算符重载Kotlin

  •  0
  • Nicola Gallazzi  · 技术社区  · 7 年前

    我刚接触过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 回复  |  直到 7 年前
        1
  •  3
  •   zsmb13    7 年前

    制造方法 in 工作是为了 third..twoThirds 调用以返回具有 contains(Rational) 方法,这就是 中的

    一种方法是返回 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 interface,然后添加 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在下面的注释中所建议的,您还可以使用此实现来避免完全转换为float:

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

    还有,你的电流 contains 实现可能会被丢弃,因为您不再需要它,而且它的功能相当奇怪。


    在这里添加除直接答案以外的内容:正如@eugene petrenko在他们的答案中建议的那样,添加几个构造函数是可行的,而不是使用 String 例如,一个需要两个 Int S,一个需要两个 BigIntegers S.P/P>

        2
  •  0
  •   Eugene Petrenko    7 年前

    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 from int s添加构造函数可能有意义,以避免解析。

    输入的返回类型不正确 rangeTo 方法,它不应该是 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 class at all实现的函数。

    compareTo 函数 rational 不太可能使用 .toFloat() 相反,您可以直接用整数实现它。