2012-02-12 8 views
7

Soy bastante nuevo en Scala. Quiero escribir varios objetos matemáticos (Complejo, Polinomio, etc.) que se cierran bajo algunas operaciones (+, -, *) de forma que se pueden usar en genéricos y se pueden usar moldes implícitos.Números de implementación

Parece que he resuelto el primer 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))) 
    } 
} 

Ahora, ¿cómo puedo escribir test() de manera que

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

vuelve complejo (7, 1)?

Respuesta

2

La idea principal es que todos los GroupUnderAddition no son compatibles, por lo que parece que quiere trabajar con álgebra compleja, sugeriría construir una superclase que incluya GoupUnderAddition. Sin embargo, no se recomienda para que sea una clase de caso (ver advertencias si tiene un case class extender 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

Claro que podría hacer esto, pero quiero mantener los genéricos. Haciéndolo a tu manera, si quisiera llamar a prueba con dos polinomios, necesitaría reescribir todo. –

+0

Sí, pero tiene 2 problemas diferentes, el primero es que desea crear el genérico 'GroupUnderAddition' que implementaría el método' + '. Por otro lado, desea tener un 'GroupUnderAddition' específico que implemente 2 tipos diferentes de números:' Real' y 'Complex'. Entonces tienes que hacer otro truco, es por eso que pensaste en 'def implícita', pero en tu caso no encontré una solución con esto. –

1

El problema es que el implicit def no se considera para la conversión argumento, a menos que especifique la definición del método de hazlo.

Por lo tanto, si tiene algo como Real(5).foo y y foo solo está definido para el complejo, el implicit def funcionará para eso.

Si tiene un método como: def foo(c : Complex) = ..., no puede llamarlo con foo(Real(5)) en su lugar.

Si desea aplicar la conversión implícita, debe especificar su método para que sus argumentos se conviertan. Para el método anterior foo que podría hacerlo de esta manera:

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

entonces es válida para llamar foo(Real(5)) y se utilizará la conversión.

Adaptado a su problema específico, se podría escribir el método de prueba como esta:

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

Especificando, que las conversiones implícitas de T a G se tendrán en cuenta, se posibilita que el método test ahora a aceptar test(Real(5), Complex(2,1)).

Sin embargo, no va a funcionar al revés todavía. Entonces todavía no puede llamarlo con test(Complex(2,1), Real(5)), porque no hay conversión implícita del segundo argumento.

Una manera directa para tener en cuenta tanto la conversión sería escribir así:

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

Por desgracia, el compilador de alguna manera se deriva Any para G cuando se llama a este método como el anterior. No sé ahora cómo resolver este problema y publiqué esta respuesta con la esperanza de que alguien más pueda completar esta última pieza del rompecabezas.

Dada la definición final por encima de al menos puede llamar al método de cualquier manera, al especificar los tipos completos:

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

El verdadero problema aquí es que usted está haciendo la suposición (incorrecta) que test(Real(5), Complex(2, 1)) está bien definido de alguna manera, dado lo que has escrito. Considere lo siguiente:

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

Esto satisface perfectamente a su definición de 'GroupUnderAddition', pero no tiene sentido tratar de añadir una palabra ("Hola") a un Real (2). ¿Cuál es el resultado?

Lo que intenta codificar es un operador de adición específico dentro de un dominio más grande, parece el dominio de polinomios sobre C, y especifica que ciertos subgrupos de este se cierran bajo el operador de suma. El enfoque de ChrisJamesC podría extenderse felizmente al anillo polinómico, que capturaría lo que usted quería.

+0

test (Real (5), Complex (2, 1)) está bien definido teniendo en cuenta que tengo una función Real => Complex implícita. No estoy tratando de hacer que esto funcione para dos hijos de GroupUnderAddition. –