【问题标题】:Getting implicit scala Numeric from Azavea Numeric从 Azavea Numeric 获取隐式 scala Numeric
【发布时间】:2012-10-29 11:30:42
【问题描述】:

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

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

【问题讨论】:

  • “除了要求所有类型都具有两个数字的上下文边界之外?”这种方法有什么问题?
  • 在我们的特定场景中,有问题的代码在其之上具有重要的层次结构,因此要避免使用 Scala 数字上下文边界污染大量类。此外,命名冲突意味着外部 API 变得更加混乱,完全限定的名称,从根本上说,Azavea Numeric 应该是 Scala Numeric 的扩展,而不是替代。
  • 这没有任何意义......
  • 抱歉,由于某种原因,它只显示了您评论的前几个字
  • 我不知道com.azavea.math.Numeric是如何定义的,但是如果它提供了与Scala的Numeric相同的功能,那么应该有一种方法可以将一个转换为另一个。你应该发布com.azavea.math.Numerics 的定义。

标签: scala generics numeric


【解决方案1】:

您可以使用 Régis Jean-Gilles 解决方案,这是一个很好的解决方案,并包装 Azavea 的 Numeric。您也可以尝试自己重新创建方法,但使用 Azavea 的 Numeric。除了 NumericRange,大多数都应该很容易实现。

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

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

【讨论】:

  • 感谢您介绍 Spire 作为 Azavea Numeric 的潜在替代品。 Azavea Numeric 是否不再处于积极开发中,还是 Spire 是替代品?我最初只是想使用 Seq.sum,但这使我们能够使用所有基于数字的方法。
  • Azavea 的数字库 (Erik Osheim) 的作者和我创建了 Spire,我们正在积极开发并替换 Azavea 的数字库。 Spire 的功能集比 Azavea 的要大得多,并且可以与 2.10 一起使用,但是 Azavea 带有一个用于 2.9 的编译器插件,它可以完成我们现在在 2.10 中使用宏所做的事情。所以,如果你被困在 2.9 并且你使用插件,那么这可能是不使用 Spire 的原因。
  • 嗨 Woodz,Erik 和我为这些转换添加了一些支持。这个想法是你可以导入 scala.math.compat._ 并且事情应该可以正常工作。如果没有,您可能需要直接使用 scala.math.compat.numeric[A]。这将是我们即将发布的 0.3.0 的一部分 :-) 感谢您的建议。 github.com/non/spire/commit/…
  • 糟糕,这是 spire.math.compat(不是 scala)。很抱歉造成混乱。
【解决方案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 类型的隐含值 类型 scala.math.Numeric 是预期的。 请注意,为了完整起见,您可能也想做相反的事情(编写一个类 ScalaNumericWrapper,根据 scala.math.Numeric 实现 com.azavea.math.Numeric)。

现在,上述解决方案有一个缺点:每次调用都会得到一个转换(因此是一个实例化)(到一个具有scala.math.Numeric 类型的上下文绑定的方法,并且你只有一个 @ 的实例987654329@ 在范围内)。 因此,您实际上需要为每种数字类型定义 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 在内部使用它 并不意味着你会得到任何速度提升,因为这里不会发生专业化)。

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

【讨论】:

    猜你喜欢
    • 2011-05-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-28
    • 1970-01-01
    • 1970-01-01
    • 2011-01-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多