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

标签 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 .

最佳答案

如果您只想使用 Scala 标准库,请查看 Numeric[T] .在您的情况下,由于您想要进行非整数除法,因此您必须使用 Fractional[T] Numeric的子类.

以下是使用 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
  }
}

但是,对于严肃的通用数值计算,我建议查看 spire .它提供了更精细的数字类型类层次结构。 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
  }
}

Spire 甚至提供从整数到 T 的自动转换如果存在 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 非常强大和通用,但也可能让初学者有些困惑。尤其是当事情不起作用时。这是一个 excellent blog post解释基本方法和一些问题。

关于scala - 如何在scala中将函数定义为所有数字的通用函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33076759/

相关文章:

scala - 未推断多参数闭包参数类型

java - 获取配置的路由的基本路径

java - 在 Spark 中获取 StreamChunkId 时失败

java - Java中的void f(Class c) 和 void f(Class<?> c) 有什么区别吗?

java - wtf.is.going.on 中的抽象方法foo(E) 无法直接访问super.foo(e) 用ANT 编译

java - 泛型工厂的泛型和类型推断

scala - Windows 上的 Zinc 服务器(scala 增量编译器)?

java - 使用 Function<T, 将 Integer 传递给泛型方法时出现编译错误?扩展号码>>

java - 什么是 PECS(生产者扩展消费者 super )?

scala - 更改列表并返回已删除内容的正确(功能)方法是什么?