2011-06-18 14 views
6

Tengo una lista [número]. Contiene valores de diferentes subclases de Número - Int, Doble, y así sucesivamente. Quiero sumar todos los valores. Si la lista solo contiene Int, me gustaría que el resultado fuera un Int. Si hay mezclas, me gustaría seguir las reglas normales de Scala: Int + Doble = Doble (y así sucesivamente).Agregar números juntos

He intentado esto:

val result = list.reduceLeft(_ + _) 

Esto no es ni siquiera compilables. El número no tiene + método definido que toma otro Número.

¿Cómo puedo lograr esto?

Andrés

+1

para referencia futura, se considera mejor forma de preguntar. una pregunta _either_ en la lista de correo de Scala _o_ StackOverflow, no ambas. Para preguntas como esta en particular lar, StackOverflow suele ser la mejor opción, ya que es más fácil para otros aprender de la pregunta y la respuesta aquí que en los archivos de la lista de correo. –

+1

Estoy parado. Eso no volverá a pasar. Gracias por toda la ayuda y por ser tan gentil en enseñarme netiqueta. – Andres

Respuesta

9

Copia de mi respuesta de la lista de correo Scala, donde la cuestión era primero preguntó:

Number es una clase Java, y no tiene ninguna funcionalidad aparte de la conversión en sí a los tipos primitivos. Desafortunadamente, el AnyVal de Scala no hace nada aparte de marcar los tipos primitivos, y Numeric[T] de Scala solo conoce su propio tipo, no una combinación de tipos.

Así que te queda una coincidencia de patrones bastante desagradable; la sintaxis más corta si está seguro es suficiente para manejar doble y int es:

list.reduceLeft((l,r) => (l: Any, r: Any) match { 
    case (li: Int, ri: Int) => li + ri 
    case _ => l.doubleValue + r.doubleValue 
}) 

Nota que estoy echando a Any para obtener el ajuste de patrones Scala para hacer el unboxing para nosotros; si lo dejas como Número, debes hacer coincidir el patrón en java.lang.Integer y luego en valueOf, lo cual es un problema.

Si quieres más se va a tener que construir en las promociones apropiadas:

list.reduceLeft((l,r) => (l: Any) match { 
    case li: Int => (r: Any) match { 
    case ri: Int => li + ri 
    case rf: Float => li + rf 
    case rl: Long => li + rl 
    case _ => li + r.doubleValue 
    } 
    case lf: Float => /* etc. */ 
}) 

Si usted tiene que hacer esto más que un poco, usted es probablemente mejor de abandonar Numeric a favor de su propio clases de envoltura que saben cómo sumarse con la promoción adecuada; luego puede escribir la coincidencia de patrón una sola vez y volver a usar + en su lista de comprensiones.

sealed abstract class Addable { 
    def +(a: Addable): Addable 
} 

final case class MyInt(value: Int) extends Addable { 
    def +(a: Addable) = a match { 
    case MyInt(i) => MyInt(value + i) 
    case MyFloat(f) => MyFloat(value + f) 
    ... 
    } 
} 

final case class MyFloat(value: Float) extends Addable { 
    def +(a: Addable) = a match { 
    case MyInt(i) => MyFloat(value + i) 
    ... 
    } 
} 

... 

Una ligera ventaja de este método es que se puede decidir para promover los valores de una manera diferente a la norma (por ejemplo, puede decidir que flotan + tiempo = doble, desde un flotador en realidad no se corta . para mantener la mayor parte de la precisión numérica del tiempo, y hacer MyDouble(value.toDouble + f.toDouble), por ejemplo

todo es bastante incómodo, ni Java ni Scala realmente proporcionan apoyo a las matemáticas de tipo mixto

0

Al reducir una lista de Double s.

No utilice la clase Number, en realidad no puede lograr nada con ella. Ni siquiera es una clase de Scala, es una clase de Java.

+0

Me pregunto por qué está degradado. Hacer algo como 'num.view.map (_. DoubleValue) .sum' es razonable aquí. – soc

+0

@SOC ¿Lo hice? Quizás fue un error. No puedo encontrarlo para desvincularme, todavía soy relativamente nuevo aquí. –

Cuestiones relacionadas