2011-06-18 12 views
6

Mam listę [liczba]. Zawiera wartości różnych podklas Number - Int, Double i tak dalej. Chcę zsumować wszystkie wartości. Jeśli lista zawiera tylko Int, chciałbym, aby wynik był int. Jeśli są miksy, chciałbym przestrzegać normalnych zasad Scala: Int + Double = Double (i tak dalej).Dodawanie liczb razem

Próbowałem to:

val result = list.reduceLeft(_ + _) 

To nie jest nawet compilable. Liczba nie ma zdefiniowanej metody +, która bierze inny numer.

Jak mogę to zrobić?

Andrés

+1

na przyszłość, jest uważana za lepszą formę zapytać. pytanie na liście mailingowej Scala _ lub _ StackOverflow, a nie na oba pytania. lar, StackOverflow jest zwykle lepszym wyborem, ponieważ łatwiej jest innym uczyć się na podstawie pytania i odpowiedzi tutaj, niż na archiwach listy mailingowej! –

+1

Stoję poprawione. To nie zdarzy się ponownie. Dziękuję za całą pomoc i za tak delikatne uczenie mnie netykiety. – Andres

Odpowiedz

9

Kopiowanie moją odpowiedź z listy mailingowej Scala, gdzie została po raz pierwszy zapytał:

Number jest klasą Java i ma funkcjonalność oprócz przekształcania się do prymitywnych typów. Niestety, Scala's AnyVal nie robi nic poza zaznaczeniem typów pierwotnych, a Numeric[T] ze Scala wie tylko o swoim typie, a nie o typie.

Dzięki temu masz dość nieprzyjemne wzorce dopasowania; najkrótsza składnia jeśli jesteś pewien, że masz tylko obsłużyć dwukrotnie i int jest:

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

Zauważ, że mam do odlewania Any uzyskać Scala wzór mecz zrobić unboxing dla nas; jeśli zostawisz go jako numer, musisz dopasować wzór na java.lang.Integer, a następnie valueOf, co jest uciążliwe.

Jeśli chcesz więcej trzeba będzie zbudować w odpowiednich promocjach:

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. */ 
}) 

Jeśli trzeba zrobić więcej niż trochę, jesteś prawdopodobnie lepiej porzucenie Numeric na rzecz własnego klasy otokowe, które wiedzą, jak dodać siebie wraz z odpowiednią promocją; możesz napisać wzór dopasowujący tylko raz i powrócić do używania + w spisie treści.

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) 
    ... 
    } 
} 

... 

Jednym niewielkim Zaletą tej metody jest to, że można zdecydować, aby promować wartości w inny sposób niż standardowy (na przykład, może zdecydować, że pływak + długo = podwójne, ponieważ pływak naprawdę nie wyciąć . zachować dużo precyzji numerycznej długo, a zrobić MyDouble(value.toDouble + f.toDouble), na przykład

to wszystko jest raczej niewygodne, ani Java ani Scala naprawdę zapewnić wsparcie dla mieszanego typu matematyki

0

poprzez zmniejszenie listę Double s.

Nie używaj klasy Number, nie możesz w rzeczywistości nic z nią zrobić. To nawet nie klasa Scala, to klasa Java.

+0

Zastanawiam się, dlaczego jesteś odrzucony. Robienie czegoś takiego jak "num.view.map (_. DoubleValue) .sum" jest tutaj rozsądne. – soc

+0

@ SOC Zrobiłem? Być może to był błąd. Nie mogę go znaleźć, aby nie głosować, wciąż jestem tu stosunkowo nowy. –