scala - 如何减少泛型方法中不需要的类型参数?

标签 scala generics

我想灵活地实现一些通用的数学函数。 例如一个名为 meandot 的函数,声明为类似

object Calc {
  def meandot[..](xs: Array[Left], ys: Array[Right])(implicit ..): Result
}

其中 meandot(xs, ys) = sum(x*y for x, y in zip(xs, ys))/length

当我在没有专门类型参数的情况下调用 meandot 时,它应该返回一个具有默认类型的值。例如

scala> Calc.meandot(Array(1, 2), Array(1, 1))
res0: Int = 1

如果我用专门的类型参数调用meandot,它可以返回一个正确的值。

scala> Calc.meandot[Int, Int, Double](Array(1, 2), Array(1, 1))
res1: Double = 1.5

但是,上面的前两个类型参数是多余的。我唯一需要专门化的类型是返回类型。我想调用它简化为

scala> Calc.meandot2(Array(1, 2), Array(1, 1))
res2: Int = 1

scala> Calc.meandot2[Double](Array(1, 2), Array(1, 1))
res3: Double = 1.5

我找到了一种方法来实现它,如下面的代码,它使用代理类 MeanDotImp但似乎没有那么优雅。所以我想知道是否有更好的解决方案来减少泛型方法中不需要的类型参数?

trait Times[L, R, N] {
  def times(x: L, y: R): N
}

trait Num[N] {
  def zero: N = fromInt(0)
  def one:  N = fromInt(1)
  def fromInt(i: Int): N
  def plus(x: N, y: N): N
  def div(x: N, y: N): N
}

abstract class LowTimesImplicits {
  implicit val IID: Times[Int, Int, Double] = new Times[Int, Int, Double] {
    def times(x: Int, y: Int): Double = x * y
  }
}

object Times extends LowTimesImplicits {
  implicit val III: Times[Int, Int, Int] = new Times[Int, Int, Int] {
    def times(x: Int, y: Int): Int = x * y
  }
}

object Num {
  implicit val INT: Num[Int] = new Num[Int] {
    def fromInt(i: Int): Int = i
    def plus(x: Int, y: Int): Int = x + y
    def div(x: Int, y: Int): Int = x / y
  }

  implicit val DOU: Num[Double] = new Num[Double] {
    def fromInt(i: Int): Double = i
    def plus(x: Double, y: Double): Double = x + y
    def div(x: Double, y: Double): Double = x / y
  }
}

object Calc {
  def meandot[L, R, N](xs: Array[L], ys: Array[R])
             (implicit t: Times[L, R, N], n: Num[N]): N = {
    val total = (xs, ys).zipped.foldLeft(n.zero){
           case(r, (x, y)) => n.plus(r, t.times(x, y))
        }
    n.div(total, n.fromInt(xs.length))
  }

  implicit class MeanDotImp[L, R](val marker: Calc.type) {
    def meandot2[N](xs: Array[L], ys: Array[R])
                (implicit t: Times[L, R, N], n: Num[N]): N = {
      val total = (xs, ys).zipped.foldLeft(n.zero){
            case(r, (x, y)) => n.plus(r, t.times(x, y))
          }
      n.div(total, n.fromInt(xs.length))
    }
  }
}

最佳答案

另一种解决方案与您的类似,但更直接一些:它首先修复您希望能够设置的类型参数,然后推断出其他两个。为此,我们可以使用 apply 方法声明一个类:

class meandot[N] {

  def apply[L, R](xs: Array[L], ys: Array[R])
    (implicit t: Times[L, R, N], n: Num[N]): N = ??? // your implementation
}

现在,为了避免编写 new meandot,我们可以定义一个只实例化此类的方法:

object Calc {

  def meandot[N]: meandot[N] = new meandot[N]
}

这种方法的优雅是有争议的,但它非常简单并且不涉及隐式。这是一个使用演示:

scala> Calc.meandot(Array(1,2,3), Array(4,5,6))
res0: Int = 10

scala> Calc.meandot[Double](Array(1,2,3), Array(4,5,6))
res1: Double = 10.666666666666666

关于scala - 如何减少泛型方法中不需要的类型参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40298181/

相关文章:

scala - Lambda 类型推断和隐式转换

java - 如何仅设置具有流式布局的面板的首选宽度?

scala - 如何知道akka中SourceQueue的已用缓冲区大小?

c# - Ninject - 管理泛型类型的不一致性?

c# - 使用泛型类型参数向上转型

c# - 强类型集合隐式和显式强制转换

java - 从 Object 转换为 List 的类型化实例的通用方法

scala - scala 中条件返回的正确习惯用法

swift - 可以快速转换 T 以符合协议(protocol)

scala - 在 Scala 工作表中打印 Futures 的结果