从Azavea数字获取隐式鳞片数字



我正在使用azavea数字scala库进行通用数学操作。但是,我不能将它们与Scala Collections API一起使用,因为它们需要Scala数字,并且似乎两个数字是相互排斥的。有什么方法可以避免重新实现Azavea Numeric的Scala集合上的所有数学操作,除了要求所有类型都具有两个数字的上下文范围吗?

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数字定义为

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]]
}

您可以使用RégisJean-Gilles解决方案,这是一个很好的解决方案,并包装Azavea的数字。您也可以尝试自己重新创建方法,但使用Azavea的数字。除数字外,大多数都应该很简单地实现。

您可能对Spire感兴趣,这成功了Azavea的数字库。它具有所有相同的功能,但还有一些新功能(更多操作,新数字类型,排序等等)。如果您使用的是2.10(我们的大多数工作是针对2.10的),则使用Spire的数字消除了几乎全部的通用方法的开销,并且通常像直接(非遗传)实现一样快。

>

也就是说,我认为您的问题是一个很好的建议;我们应该真正在数字上添加toScalaNumeric方法。您打算使用哪种Scala收集方法?Spire为阵列添加了几种新方法,例如Qsum,Qproduct,Qnorm(p),Qsort,Qselect(k)等。

最一般的解决方案是编写包装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的隐式值预期scala.math.Numeric类型。请注意,要完成,您可能也需要做反向(编写一个以scala.math.numeric来实现com.azavea.math.numeric的类scalanumericwrapper。

现在,上述解决方案存在缺点:您在每个呼叫上获得转换(因此是一个实例化)(到具有scala.math.Numeric类型上下文的方法,而您只有com.azavea.math.Numeric的实例在其中范围)。因此,您实际上需要为每种数字类型定义AzeveAnumericWrapper的隐式单例实例。假设您有类型的MyTypeMyOtherType,则为com.azavea.math.Numeric的实例定义的实例:

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

另外,请记住,Azavea数字类的明显主要目的是极大地提高执行速度(主要是由于类型参数专业化)。使用上面的包装器,您将失去专业化,因此将其产生的速度。必须一直使用专业化,而且,一旦您调用一个不专业的通用方法,就可以进入无专门化的仿制药的世界(即使该方法又回电了一种专业方法)。因此,在速度重要的情况下,尝试直接使用Azavea的Numeric而不是Scala的Numeric(仅仅是因为AzaveAveAnumericWrapper内部使用它并不意味着您将获得任何速度提高,因为这里不会发生专业化)。

您可能已经注意到,我在示例中避免了IntLong等类型的AzaveaNumericWrapper实例。这是因为Scala.math.numeric的这些类型的隐式值(在标准库中)已经存在。您可能很想隐藏它们(通过import scala.math.Numeric.{ShortIsIntegral => _}之类的东西),以确保使用自己的(Azavea备份)版本,但是没有意义。我能想到的唯一原因是使其运行更快,但是如上所述,它不会。

最新更新