一起添加数字

时间:2011-06-18 17:22:47

标签: scala

我有一个List [Number]。它包含Number - Int,Double等不同子类的值。我想把所有的价值加在一起。如果列表只包含Int,我希望结果是Int。如果有混音,我想遵循正常的Scala规则:Int + Double = Double(依此类推)。

我试过这个:

val result = list.reduceLeft(_ + _)

这甚至无法编译。 Number没有定义带有另一个Number的+方法。

我该如何做到这一点?

安德烈斯

2 个答案:

答案 0 :(得分:9)

从首次提出问题的Scala邮件列表中复制我的答案:

Number是一个Java类,除了将自身转换为原始类型之外没有任何功能。不幸的是,除了标记原始类型之外,Scala的AnyVal没有做任何事情,Scala的Numeric[T]只知道它自己的类型,而不是类型的混合。

所以你留下了相当不愉快的模式匹配;最短的语法,如果你确定你只需要处理double和int是:

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

请注意,我正在转向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. */
})

如果你不得不这么做,你可能最好放弃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)
    ...
  }
}

...

这种方法的一个小优点是你可以决定以不同于标准的方式推广价值(例如,你可能决定浮点数+长=双倍,因为浮动确实没有被切断以保持大部分long的数字精度,例如MyDouble(value.toDouble + f.toDouble)

这一切都很尴尬; Java和Scala都没有真正为混合型数学提供支持。

答案 1 :(得分:0)

缩小Double s。

列表

不要使用班级Number,你实际上无法用它完成任何事情。它甚至不是Scala类,它是一个Java类。