2012-02-12 13 views
7

Sono abbastanza nuovo per Scala. Voglio scrivere diversi oggetti matematici (Complesso, Polinomiale, ecc.) Che sono chiusi sotto alcune operazioni (+, -, *) in modo che possano essere usati in generici e possono essere usati cast impliciti.Numeri di implementazione

Mi sembra di aver risolto il primo bit.

trait GroupUnderAddition[T] { 
    def + (t : T) : T 
} 

case class Real(d : Double) extends GroupUnderAddition[Real] { 
    def + (r : Real) = Real(d + r.d) 
} 

case class Complex(re : Double, im : Double) extends GroupUnderAddition[Complex] { 
    def + (c : Complex) = Complex(re + c.re, im + c.im) 
} 

object Test { 
    implicit def real_to_complex(r : Real) = Complex(r.d, 0) 

    def test[G <: GroupUnderAddition[G]](a : G, b : G) = a + b 

    def main(args : Array[String]) { 
    println(test(Real(5), Real(2))) 
    } 
} 

Ora, come posso scrivere test() in modo che

test(Real(5), Complex(2, 1)) 

rendimenti Complex (7, 1)?

risposta

2

L'idea principale è che tutti GroupUnderAddition non sono compatibili, in modo ti sembra di essere che vogliono lavorare con l'algebra complesso, vorrei suggerire di costruire una superclasse compreso GoupUnderAddition. Tuttavia non è consigliabile per rendere una classe case (vedi avvisi se si dispone di un case class estendere una case class)

trait GroupUnderAddition[T] { 
    def + (t : T) : T 
} 

class ComplexAlgebra(_re:Double, _im:Double) extends(GroupUnderAddition[ComplexAlgebra]) { 
    val re = _re 
    val im = _im  
    def + (c : ComplexAlgebra) = new ComplexAlgebra(re + c.re, im + c.im) 
} 

case class Real(d : Double) extends ComplexAlgebra(d, 0) 

case class Complex(real : Double, imaginary : Double) extends ComplexAlgebra(real,imaginary) 

object Test { 

    def test(a : ComplexAlgebra, b : ComplexAlgebra) = a + b 

    def main(args : Array[String]) { 
    println(test(Real(5), Real(2))) 
    } 
} 
+0

Certo che potrei fare questo, ma voglio mantenere i farmaci generici. Facendolo a modo tuo, se volessi chiamare test con due polinomi, dovrei riscrivere il tutto. –

+0

Sì, ma hai 2 diversi problemi, il primo è che vuoi creare il generico 'GroupUnderAddition' che implementerebbe il metodo' + '. D'altra parte si desidera avere uno specifico 'GroupUnderAddition' che implementa 2 diversi tipi di numeri:' Real' e 'Complex'. Quindi hai un altro trucco, è per questo che hai pensato a "implicit def", ma nel tuo caso non ho trovato una soluzione con questo. Il test –

1

Il problema è che il implicit def non è considerato per la conversione argomento, se non si specifica la definizione del metodo di fare così.

Quindi, se si dispone di qualcosa come Real(5).foo e foo è definito solo per complesso, il implicit def funzionerà per quello.

Se si dispone di un metodo come: def foo(c : Complex) = ... non si può chiamare con foo(Real(5)) invece.

Se si desidera applicare la conversione implicita, è necessario specificare il metodo in modo che i relativi argomenti possano essere convertiti. Per il metodo di cui sopra foo si potrebbe fare in questo modo:

def foo[T](c : T)(implicit ct : T => Complex) = ...` 

allora è valida per chiamare foo(Real(5)) e saranno utilizzati la conversione.

Adattato al vostro problema specifico, si potrebbe scrivere il metodo di test come questo:

def test[G <: GroupUnderAddition[G],T](a : T, b : G)(implicit ag: T => G) = a + b 

Specificando, che le conversioni implicite da T a G devono essere presi in considerazione, si consente il metodo test di accettare ora test(Real(5), Complex(2,1)).

Tuttavia, non funzionerà ancora al contrario. Quindi non puoi ancora chiamarlo con test(Complex(2,1), Real(5)), perché non c'è alcuna conversione implicita del secondo argomento.

Un modo semplice per tenere conto sia per la conversione sarebbe quella di scrivere in questo modo:

def test[G <: GroupUnderAddition[G],T1, T2](a : T1, b : T2)(implicit ag: T1 => G, bg: T2 => G) = a + b 

Purtroppo, il compilatore deriva in qualche modo Any per G quando si chiama questo metodo come sopra. Non so in questo momento, come risolvere questo problema e ho postato questa risposta nella speranza che qualcun altro possa riempire questo ultimo pezzo del puzzle.

Data la definizione finale di cui sopra si può almeno chiamare il metodo in entrambi i casi, quando si specificano i tipi di completi:

println(test[Complex,Real,Complex](Real(5), Complex(2, 1))) 
println(test[Complex,Complex,Real](Complex(2,1), Real(5))) 
1

Il vero problema qui è che si sta facendo l'ipotesi (errata) che test(Real(5), Complex(2, 1)) è ben definito in qualsiasi modo, dato ciò che hai scritto. Si consideri il seguente:

case class Word(s : String) extends GroupUnderAddition[Word] { 
    def +(w : Word) = Word(s + w.s) 
} 

Questo soddisfa perfettamente la tua definizione di 'GroupUnderAddition', ma non ha senso cercare di aggiungere una parola ("Ciao") per un Real (2). Qual è il risultato?

Quello che stai cercando di codificare è un operatore di addizione specifico all'interno di un dominio più grande - sembra il dominio dei polinomi su C - e specifica che determinati sottogruppi di questo sono chiusi sotto l'operatore di addizione. L'approccio di ChrisJamesC potrebbe essere felicemente esteso all'anello polinomiale, che catturerebbe ciò che volevi.

+0

(Real (5), Complex (2, 1)) è ben definito considerando che ho una funzione implicita Real => Complex. Non sto cercando di farlo funzionare per due figli di GroupUnderAddition. –