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
。
答案 0 :(得分:9)
如果您只想使用scala标准库,请查看Numeric[T]
。在您的情况下,由于您想要进行非整数除法,您必须使用Numeric
的{{3}}子类。
以下是使用scala标准库类型类的代码的外观。请注意Fractional
从Ordered
延伸。在这种情况下这很方便,但它在数学上也不是通用的。例如。您无法为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解释基本方法和一些问题。