public
Created

Complex numbers.

  • Download Gist
Complex.scala
Scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
package com.czechscala.blank
 
class Complex(val r: Double, val i: Double) {
 
def this(coord: Tuple2[Double, Double]) = this(
coord._1 * Math.cos(coord._2),
coord._1 * Math.sin(coord._2)
)
 
def +(other: Complex) = new Complex(r + other.r, i + other.i)
 
def -(other: Complex) = new Complex(r - other.r, i - other.i)
 
def *(other: Complex) = new Complex(
r * other.r - i * other.i,
i * other.r + r * other.i
)
 
def /(other: Complex) = new Complex(
(r * other.r + i * other.i) / (other.r * other.r + other.i * other.i),
(i * other.r - r * other.i) / (other.r * other.r + other.i * other.i)
)
 
override def equals(obj: scala.Any) = obj match {
case that: Complex => Math.abs((r - that.r) / r) <= 0.001 && Math.abs((i - that.i) / i) <= 0.001
case _ => false
}
 
override def toString = {
val sign = if (i < 0) '-' else '+'
s"($r $sign ${Math.abs(i)}i)"
}
}
ComplexTest.scala
Scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
package com.czechscala.blank
 
import org.scalatest.FunSuite
 
class ComplexTest extends FunSuite {
 
trait ComplexTrait {
val c1 = new Complex(1, 2)
val c2 = new Complex(3, 4)
}
 
test("constructor") {
val comp = new Complex(1, 2)
assert(comp.r === 1)
assert(comp.i === 2)
}
 
test("simple adding") {
new ComplexTrait {
val c3 = new Complex(4, 6)
assert(c1 + c2 === c3)
}
}
 
test("simple subtraction") {
new ComplexTrait {
val c3 = new Complex(-2, -2)
assert(c1 - c2 === c3)
}
}
 
test("simple multiplication") {
new ComplexTrait {
val c3 = new Complex(-5, 10)
assert(c1 * c2 === c3)
}
}
 
test("simple division") {
new ComplexTrait {
val c3 = new Complex(0.44, 0.08)
assert(c1 / c2 === c3)
}
}
 
test("toString") {
new ComplexTrait {
assert(c1.toString === "(1.0 + 2.0i)")
assert(new Complex(-1, 2).toString === "(-1.0 + 2.0i)")
assert(new Complex(-1, -2).toString === "(-1.0 - 2.0i)")
}
}
 
test("equals") {
new ComplexTrait {
assert(c1 == new Complex(c1.r, c1.i))
assert(c1 != c2)
assert(c1 != new Object())
assert(c1 != true)
assert(new Complex(1.00001, 1.00004) == new Complex(1, 1))
}
}
 
test("construct with polar coordinates") {
val c = new Complex((5, 0.92729))
assert(c === new Complex(3, 4))
assert(c != new Complex(3, 40))
}
}

Mám tu dvě poznámky. K první doporučuji si něco najít o case classes, hodí se skoro všude. Druhá poznámka bude zajímavá až při řešení syntaxe typu 2+3.i.

  1. Metoda equals by šla vyřešit pomocí case classes - začátek deklarace třídy by byl case class Complex(r: Double, i: Double) a dostali byste "zdarma" metodu equals plus další metody (např. hashCode, na které jste zapomněli).

  2. Tady to zatím není moc vidět, ale pokud bychom chtěli umožnit zápis 3+2.i, museli bychom udělat dvě* implicitní konverze (řekněme realToComplex pro operace mezi reálnými a komplexními čísly a k tomu realComplexConversions pro výraz 2.i) hodilo by se přejmenovat field i na něco jiného. Jinak by asi vznikla nejednoznačnost u implicitních konverzí - 2.i by se mohlo vyhodnotit
    a) buď jako realToComplex(2).i (tedy by se to snažilo zjistit imaginární složku z dvojky, která by samozřejmě byla nulová, tedy 0.0)
    b) nebo jako realComplexConversions(2).i, což by mělo vrátit Complex(0, 2).

*) OK, zanedbávám, že reálné číslo může být vyjádřeno nejen typem Double, ale i Float nebo dokonce Int apod. Pro zjednodušení.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.