Domanda

I want to write a class which can work nicely with numbers and operators, so I want to know how to overload an operator when the left hand argument is a built-in type or other value for which I can't modify the implementation.

class Complex(val r:Double,val i:Double){
  def +(other:Complex) = new Complex(r+other.r,i+other.i)
  def +(other:Double) = new Complex(r+other,i)
  def ==(other:Complex) = r==other.r && i==other.i
}

With this example, the following works:

val c = new Complex(3,4)
c+2 == new Complex(5,4)  //true

But I also want to be able to write

2+c

which doesn't work as Int.+ can't accept an argument of type Complex. Is there a way to let me write this and have it work as I want?

I've found out about right associative methods, but they seem to require a different operator name.

È stato utile?

Soluzione 2

Using implicit conversions to turn Int into Complex as mentioned before will do the job.

Here's a working solution which puts it all together to complement Ivan's answer:

import scala.language.implicitConversions

class Complex(val real:Double, val imaginary:Double){
    def +(other:Complex) = new Complex(real+other.real, imaginary+other.imaginary)
    //def +(other:Double) = new Complex(real+other,imaginary) // Not needed now
    def ==(other:Complex) = real==other.real && imaginary==other.imaginary
    override def toString: String = s"$real + ${imaginary}i"
}

object Complex {
    implicit def intToComplex(real: Int): Complex = doubleToComplex(real.toDouble)
    implicit def doubleToComplex(real: Double): Complex = Complex(real, 0)

    implicit def apply(real: Double, imaginary: Double): Complex = new Complex(real, imaginary)
    implicit def apply(tuple: (Double, Double)): Complex = Complex(tuple._1, tuple._2)

    def main(args: Array[String]) {
        val c1 = Complex(1, 2)
        println(s"c1: $c1")

        val c2: Complex = (3.4, 4.2) // Implicitly convert a 2-tuple
        println(s"c2: $c2")

        val c3 = 2 + c1
        println(s"c3: $c3")

        val c4 = c1 + 2 // The 2 is implicitly converted then complex addition is used
        println(s"c4: $c4")
    }
}

Some notes:

  • tested under 2.10.3. Depending on your version you might need the import at the top of the file.
  • once you have implicit conversions, you don't actually need the original + method which takes an Int input (hence I commented it out). Example c4 proves this.
  • consider making your class generic - that is instead of using Double, just use some numerical type which is broad enough for your needs. The concept of a complex number is actually completely decoupled from the field/ring/group it extends. For example, if your underlying type has multiplication and addition, you can define multiplication and addition for complex numbers too.
  • I added an implicit conversion for the Tuple2[Double, Double] class - just because it seemed cool. Example c2 demonstrates it.
  • similarly I added apply methods. Consider adding unapply methods to make it more case friendly.
  • I renamed your constructor arguments simply to make the toString method less confusing (so you don't have ${i}i)
  • (side track) you're == method is comparing type Double, not Int, so you'll get all the usual frustration and unexpected behaviour with floating point comparisons

Altri suggerimenti

You can add an implicit conversion from Int to Complex:

implicit def intToComplex(real: Int) = new Complex(real, 0)

You can read about implicits (e.g. here: Understanding implicit in Scala). The short version is that, if your program doesn't type check, all implicits that are in the current scope will be tried and if some of them works, it will be applied by the compiler.

So, when you write 2 + c, the compiler doesn't find an operator + that takes a complex in Int, so it tries the implicits. It will then compile 2 + c to intToComplex(2) + c which will work correctly.

From section 6.2.3 of Scala Reference

The associativity of an operator is determined by the operator's last character. Operators ending in a colon ':' are right-associative. All other operators are left-associative.

So the closest you can get is 2 +: c

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top