为什么我不能将这种类型转换为通用类型?

Why can't I convert this type into a generic one?

def linearInterpolation(weights: Seq[Double], points: Seq[Seq[Double]]) : Seq[T] = {

  weights.zip(points).map(
    weight_point => weight_point._2.map(coordinate => weight_point._1 * coordinate)
  ).reduce((point_a : Seq[Double], point_b : Seq[Double]) => point_a.zip(point_b).map(coordinate_points => (coordinate_points._1 + coordinate_points._2).asInstanceOf[T]))

}

在此代码中,我尝试将 return 的类型 Seq[Double] 转换为 Seq[T]。在执行调用时,T 可能是例如DoubleInt.

这个转换应该感谢.asInstanceOf[T]

错误

代码编译。

但是如果我执行它,我会得到以下错误:

Error:(25, 61) type mismatch; found : (Seq[Double], Seq[Double]) => Seq[T] required: (Seq[Any], Seq[Any]) => Seq[Any] ).reduce((point_a : Seq[Double], point_b : Seq[Double]) => point_a.zip(point_b).map(coordinate_points => (coordinate_points._1 + coordinate_points._2).asInstanceOf[T]))

Error:(25, 13) type mismatch; found : Seq[Any] required: Seq[T] ).reduce((point_a : Seq[Double], point_b : Seq[Double]) => point_a.zip(point_b).map(coordinate_points => (coordinate_points._1 + coordinate_points._2).asInstanceOf[T]))

问题

为什么会执行失败?如何实现从 Seq[Double]Seq[T] 的转换?

首先,不,这段代码无法编译:那些是编译错误,不是运行时异常。你可以看到,因为它以 Error: 开头,指向源代码中的确切位置(异常只有行号),并且没有堆栈跟踪。

现在,如果它确实编译了,它就不会工作,但这是一个单独的问题。

那么为什么它不能编译呢? weights.zip(points).map(...)的类型是Seq[Seq[Double]],所以reduce的签名变成了reduce[A1 >: Seq[Double]](op: (A1, A1) => A1): A1。请注意 return 和 reduce 参数的参数类型必须匹配,而在您的情况下它们不匹配(您有 (Seq[Double], Seq[Double]) => Seq[T])。这本身就足以不编译。

整个weights.zip(points).map(...).reduce(...)的预期类型是Seq[T],所以编译器需要选择A1,即:

  1. Seq[Double] 的超类型以满足约束

  2. Seq[T] 的子类型,使 return 类型匹配

这样的类型不存在(对 T 没有额外的限制),但如果存在,它将是 Seq[SomeType],这是编译器应该得到的。为什么它最终显示 Any,我真的不明白。

How to achieve this conversion from a Seq[Double] to a Seq[T] ?

如果你有 weights: Seq[T], points: Seq[Seq[T]] 就更有意义了。在这种情况下,使用 Numeric。 Stack Overflow 和外部有很多答案解释如何,例如Scala equivalent of Java's Number.

对于weights: Seq[Double], points: Seq[Seq[Double]],我会添加一个函数Double => T作为额外的参数:

def linearInterpolation(weights: Seq[Double], points: Seq[Seq[Double]])(fromDouble: Double => T) : Seq[T] = {

  weights.zip(points).map(
    weight_point => weight_point._2.map(coordinate => weight_point._1 * coordinate)
  ).reduce((point_a : Seq[Double], point_b : Seq[Double]) => point_a.zip(point_b).map(coordinate_points => coordinate_points._1 + coordinate_points._2)).map(fromDouble)

}

我补充了 Alexey 的回答。 我会将 fromDouble 声明为隐式并将转换器函数创建为隐式值,您可以按设计使用函数。 例如:

object DoubleConverters {
  implicit val doubleToDouble: Double => Double = identity
  implicit val doubleToInt: Double => Int = _.round.toInt
}

def linearInterpolation[T](weights: Seq[Double], points: Seq[Seq[Double]])(implicit converter: Double => T) : Seq[T] =
  weights.zip(points)
     .map(weight_point => weight_point._2.map(coordinate => weight_point._1 * coordinate))
     .reduce((point_a: Seq[Double], point_b: Seq[Double]) => point_a.zip(point_b).map(coordinate_points => (coordinate_points._1 + coordinate_points._2)))
     .map(converter)

// sample call
val result = linearInterpolation[Double]( weights = Seq(1.1, 1.3), Seq(Seq(1.1, 1.2), Seq(1.3, 1.4)))

//示例调用 val result = linearInterpolation[Double]( weights = Seq(1.1, 1.3), Seq(Seq(1.1, 1.2), Seq(1.3, 1.4)))

因此与类型 class 具有相同的效果:

trait DoubleConverter[T] {
    def convert(d: Double): T
}

object DoubleConverter {
    implicit val doubleToInt: DoubleConverter[Int] = new DoubleConverter[Int] {
        override def convert(d: Double): Int = d.round.toInt
    }

    implicit val doubleToDouble: DoubleConverter[Double] = new DoubleConverter[Double] {
        override def convert(d: Double): Double = d
    }
}

def linearInterpolation[T](weights: Seq[Double], points: Seq[Seq[Double]])(implicit converter: DoubleConverter[T]) : Seq[T] =
    weights.zip(points)
       .map(weight_point => weight_point._2.map(coordinate => weight_point._1 * coordinate))
       .reduce((point_a: Seq[Double], point_b: Seq[Double]) => point_a.zip(point_b).map(coordinate_points => (coordinate_points._1 + coordinate_points._2)))
       .map(converter.convert)

// sample call
val result = linearInterpolation[Int]( weights = Seq(1.1, 1.3), Seq(Seq(1.1, 1.2), Seq(1.3, 1.4)))

看到用法保持不变。我们避免意外使用 Double => Int。