我有一个List [Number].它包含Number - Int,Double等不同子类的值.我想把所有的价值加在一起.如果列表只包含Int,我希望结果是Int.如果有混音,我想遵循正常的Scala规则:Int + Double = Double(依此类推).
我试过这个:
val result = list.reduceLeft(_ + _)
Run Code Online (Sandbox Code Playgroud)
这甚至不可编译.Number没有定义带有另一个Number的+方法.
我怎么能做到这一点?
安德烈斯
从首次提出问题的Scala邮件列表中复制我的答案:
Number是一个Java类,除了将自身转换为原始类型之外没有任何功能.不幸的是,AnyVal除了标记原始类型之外,Scala 没有做任何事情,并且Numeric[T]Scala只知道它自己的类型,而不是类型的混合.
所以你留下了相当不愉快的模式匹配; 最短的语法,如果你确定你只需要处理double和int是:
list.reduceLeft((l,r) => (l: Any, r: Any) match {
case (li: Int, ri: Int) => li + ri
case _ => l.doubleValue + r.doubleValue
})
Run Code Online (Sandbox Code Playgroud)
请注意,我正在Any为了获得Scala模式匹配而为我们进行拆箱操作; 如果你把它作为数字,你必须模式匹配java.lang.Integer然后valueOf它,这是一个痛苦.
如果你想要更多,你必须建立适当的促销活动:
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. */
})
Run Code Online (Sandbox Code Playgroud)
如果你不得不多做一点,你可能最好放弃Numeric支持你自己的包装类,这些类知道如何通过适当的推广将自己添加到一起; 那么你可以只编写匹配一次的模式,然后在列表推导中返回使用+.
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)
...
}
}
...
Run Code Online (Sandbox Code Playgroud)
这种方法的一个小优点是你可以决定以不同于标准的方式提升值(例如,你可能决定float + long = double,因为浮点数实际上并没有被删除以保持很长的数值精度,MyDouble(value.toDouble + f.toDouble)例如,做.
这一切都很尴尬; Java和Scala都不能真正为混合型数学提供支持.