如何在scala中的所有数字中将函数定义为通用函数?

时间:2015-10-12 08:43:07

标签: scala generics

I thought我需要在所有Ordering[_]类型中对我的函数进行参数化。但这并不奏效。

如何使以下功能适用于所有支持所需数学运算的类型,我怎么能自己找到它?

  /**
   * Given a list of positive values and a candidate value, round the candidate value
   * to the nearest value in the list of buckets.
   *
   * @param buckets
   * @param candidate
   * @return
   */
  def bucketise(buckets: Seq[Int], candidate: Int): Int = {

    // x <= y
    buckets.foldLeft(buckets.head) { (x, y) =>
      val midPoint = (x + y) / 2f

      if (candidate < midPoint) x else y
    }
  }

我尝试在intellij中点击数学运算符(/+),但只是注意到Sc synthetic function

1 个答案:

答案 0 :(得分:9)

如果您只想使用scala标准库,请查看Numeric[T]。在您的情况下,由于您想要进行非整数除法,您必须使用Numeric的{​​{3}}子类。

以下是使用scala标准库类型类的代码的外观。请注意FractionalOrdered延伸。在这种情况下这很方便,但它在数学上也不是通用的。例如。您无法为Fractional[T]定义Complex,因为它未被订购。

def bucketiseScala[T: Fractional](buckets: Seq[T], candidate: T): T = {
  // so we can use integral operators such as + and /
  import Fractional.Implicits._
  // so we can use ordering operators such as <. We do have a Ordering[T] 
  // typeclass instance because Fractional extends Ordered
  import Ordering.Implicits._
  // integral does not provide a simple way to create an integral from an 
  // integer, so this ugly hack
  val two = (implicitly[Fractional[T]].one + implicitly[Fractional[T]].one)
  buckets.foldLeft(buckets.head) { (x, y) =>
    val midPoint = (x + y) / two
    if (candidate < midPoint) x else y
  }
}

然而,对于严肃的通用数值计算,我建议看看Fractional[T]。它提供了更复杂的数字类型层次结构。 Spire类型类也是专门的,因此通常与直接使用基元一样快。

以下是如何使用示例使用spire:

// imports all operator syntax as well as standard typeclass instances
import spire.implicits._
// we need to provide Order explicitly, since not all fields have an order. 
// E.g. you can define a Field[Complex] even though complex numbers do not 
// have an order.
def bucketiseSpire[T: Field: Order](buckets: Seq[T], candidate: T): T = {
  // spire provides a way to get the typeclass instance using the type 
  // (standard practice in all libraries that use typeclasses extensively)
  // the line below is equivalent to implicitly[Field[T]].fromInt(2)
  // it also provides a simple way to convert from an integer
  // operators are all enabled using the spire.implicits._ import
  val two = Field[T].fromInt(2)
  buckets.foldLeft(buckets.head) { (x, y) =>
    val midPoint = (x + y) / two
    if (candidate < midPoint) x else y
  }
}

如果存在T,Spire甚至会提供从整数到Field[T]的自动转换,因此您甚至可以像这样编写示例(几乎与非泛型版本相同)。但是,我认为上面的例子更容易理解。

// this is how it would look when using all advanced features of spire
def bucketiseSpireShort[T: Field: Order](buckets: Seq[T], candidate: T): T = {
  buckets.foldLeft(buckets.head) { (x, y) =>
    val midPoint = (x + y) / 2
    if (candidate < midPoint) x else y
  }
}

更新:spire是非常强大和通用的,但也可能有点混淆初学者。特别是当事情不起作用时。这是spire解释基本方法和一些问题。