从Azavea Numeric获取隐式scala数字

时间:2012-10-29 11:30:43

标签: scala generics numeric

我使用Azavea Numeric Sc​​ala库进行通用数学运算。但是,我不能将它们与Scala Collections API一起使用,因为它们需要一个scala Numeric,看起来好像两个Numerics是互斥的。有什么方法可以避免重新实现对Azavea Numeric的Scala Collections的所有数学运算,除了要求所有类型都有Numerics的上下文边界?

import Predef.{any2stringadd => _, _}

class Numeric {
  def addOne[T: com.azavea.math.Numeric](x: T) {
    import com.azavea.math.EasyImplicits._
    val y = x + 1 // Compiles

    val seq = Seq(x)
    val z = seq.sum // Could not find implicit value for parameter num: Numeric[T]
  }
}

Azavea Numeric定义为

trait Numeric[@scala.specialized A] extends java.lang.Object with 

com.azavea.math.ConvertableFrom[A] with com.azavea.math.ConvertableTo[A] with scala.ScalaObject {
   def abs(a:A):A
   ...remaining methods redacted...
}

object Numeric {
  implicit object IntIsNumeric extends IntIsNumeric
  implicit object LongIsNumeric extends LongIsNumeric
  implicit object FloatIsNumeric extends FloatIsNumeric
  implicit object DoubleIsNumeric extends DoubleIsNumeric
  implicit object BigIntIsNumeric extends BigIntIsNumeric
  implicit object BigDecimalIsNumeric extends BigDecimalIsNumeric

  def numeric[@specialized(Int, Long, Float, Double) A:Numeric]:Numeric[A] = implicitly[Numeric[A]]
}

2 个答案:

答案 0 :(得分:4)

您可以使用RégisJean-Gilles解决方案,这是一个很好的解决方案,并包裹Azavea的数字。您也可以尝试自己重新创建方法,但使用Azavea的Numeric。除了NumericRange之外,大多数应该非常简单易用。

你可能对Spire感兴趣,但是它接替了Azavea的数字库。它具有所有相同的功能,但也有一些新功能(更多操作,新数字类型,排序和选择等)。如果您使用的是2.10(我们的大多数工作都是针对2.10),那么使用Spire的Numeric几乎可以消除通用方法的所有开销,并且通常与直接(非通用)实现一样快。

那就是说,我认为你的问题是一个很好的建议;我们应该在Numeric上添加toScalaNumeric方法。您计划使用哪种Scala收集方法? Spire为数组添加了几种新方法,如qsum,qproduct,qnorm(p),qsort,qselect(k)等。

答案 1 :(得分:2)

最常见的解决方案是编写一个包含com.azavea.math.Numeric并按其实现scala.math.Numeric的类:

class AzaveaNumericWrapper[T]( implicit val n: com.azavea.math.Numeric[T] ) extends scala.math.Numeric {
  def compare (x: T, y: T): Int = n.compare(x, y)
  def minus (x: T, y: T): T = n.minus(x, y)
  // and so on
}

然后实现隐式转换:

// NOTE: in scala 2.10, we could directly declare AzaveaNumericWrapper as an implicit class
implicit def toAzaveaNumericWrapper[T]( implicit n: com.azavea.math.Numeric[T] ) = new AzaveaNumericWrapper( n )

n本身是一个隐含的事实在这里是关键:它允许自动使用类型为com.azavea.math.Numeric的隐式值,其中na隐含值为 类型scala.math.Numeric是预期的。 请注意,要完成,您可能也想反过来(编写一个ScalaNumericWrapper类,根据scala.math.Numeric实现com.azavea.math.Numeric。)

现在,上述解决方案存在一个缺点:每次调用都会得到转换(因而就是实例化)(对于具有类型scala.math.Numeric的上下文绑定的方法,并且只有一个实例com.azavea.math.Numeric的范围)。 因此,您实际上希望为每种数字类型定义AzaveaNumericWrapper的隐式单例实例。假设您有MyTypeMyOtherType类型,您为其定义了com.azavea.math.Numeric的实例:

implicit object MyTypeIsNumeric extends AzaveaNumericWrapper[MyType]
implicit object MyOtherTypeIsNumeric extends AzaveaNumericWrapper[MyOtherType]
//...

另外,请记住,azavea的Numeric类的明显主要目的是大大提高执行速度(主要是由于类型参数专门化)。 使用上面的包装器,你会失去专业化,从而失去它的速度。专业化必须一直使用, 一旦你调用一个非专业的泛型方法,你就进入非特殊泛型的世界(即使该方法然后回调一个专门的方法)。 因此,在速度很重要的情况下,尝试直接使用azavea的Numeric而不是scala的Numeric(因为AzaveaNumericWrapper在内部使用它) 并不意味着你会得到任何速度增加,因为这里不会发生专业化。)

您可能已经注意到,我在示例中避免为类型AzaveaNumericWrapperInt定义Long的实例,依此类推。 这是因为对于这些类型,已经存在(在标准库中)scala.math.Numeric的隐式值。 您可能想要隐藏它们(通过import scala.math.Numeric.{ShortIsIntegral => _}之类的东西),以确保使用您自己的(azavea支持的)版本, 但没有意义。我能想到的唯一原因是让它跑得更快,但如上所述,它不会。