1

我有一个通用案例类 Route,它包含 Location 的子类列表。但是在以下方法中,我在调用中遇到类型不匹配distance expected: head.T, actual: T

case class Route[T <: Location](route: List[T]) {
  def measureDistance: Double = {
    def measure(head: T, tail: List[T], acc: Double = 0.0): Double = tail match {
      case Nil => acc
      case h :: t => measure(h, t, head.distance(h) + acc)
    }
    if (route.isEmpty) 0.0
    else measure(route.head, route.tail)
  }
}

基本的抽象Location类如下

abstract class Location(val name: String) {

type T <: Location

def distance(that: T): Double
}

由于 head 和 h 都来自同一个列表route,我不明白为什么它们不是同一类型。

4

3 回答 3

3

在这种情况下,您想要的似乎是 F 有界多态性:

abstract class Location[L <: Location[L]](val name: String) {
  def distance(that: L): Double
}

case class Route[T <: Location[T]](route: List[T]) {
  def measureDistance: Double = {
    def measure(head: T, tail: List[T], acc: Double = 0.0): Double = tail match {
      case Nil => acc
      case h :: t => measure(h, t, head.distance(h) + acc)
    }
    if (route.isEmpty) 0.0
    else measure(route.head, route.tail)
  }
}

但是,您也可以考虑使用Metric-typeclass 代替:

trait Metric[L] {
  def dist(a: L, b: L): Double
}

case class Route[T: Metric](route: List[T]) {
  def measureDistance: Double = {
    def measure(head: T, tail: List[T], acc: Double = 0.0): Double = tail match {
      case Nil => acc
      case h :: t => measure(h, t, implicitly[Metric[T]].dist(head, h) + acc)
    }
    if (route.isEmpty) 0.0
    else measure(route.head, route.tail)
  }
}

后一种解决方案将适用于更多类型,例如 to (Double, Double),即使它们不继承自Location.

这里又是 typeclass 解决方案,但使用了稍微更精致的 Cats 风格的语法,避免了implicitly

trait Metric[L] {
  def dist(a: L, b: L): Double
}

object Metric {
  def apply[T](implicit m: Metric[T]): Metric[T] = m
}

case class Route[T: Metric](route: List[T]) {
  def measureDistance: Double = {
    def measure(head: T, tail: List[T], acc: Double = 0.0): Double = tail match {
      case Nil => acc
      case h :: t => measure(h, t, Metric[T].dist(head, h) + acc)
    }
    if (route.isEmpty) 0.0
    else measure(route.head, route.tail)
  }
}
于 2018-06-08T09:53:15.007 回答
0

您无需TLocation抽象类中定义类型。您应该按以下步骤进行:

abstract class Location[T <: Location[T]](val name: String) {
  def distance(that: T): Double
}

case class Route[T <: Location[T]](route: List[T]) {
  def measureDistance: Double = {
    def measure(head: T, tail: List[T], acc: Double = 0.0): Double = tail match {
      case Nil => acc
      case h :: t => measure(h, t, head.distance(h) + acc)
    }
    if (route.isEmpty) 0.0
    else measure(route.head, route.tail)
  }
}
于 2018-06-08T09:53:56.627 回答
0

scala 编译器无法知道type T <: Locationclass 中定义的类型与 RouteLocation的类型参数相同[T <: Location]

我想你将不得不 chage 的签名def distance(...)。我不确定,但如果将 T 定义为 Location 的类型参数,它应该可以工作:

abstract class Location[T <: Location[T]](val name: String) {
  def distance[T](that: T): Double
}
于 2018-06-08T09:55:47.633 回答