我正在做一个简单的练习来构建自己的List。我想定义一个将常规列表转换为MyList
的方法。递归版似乎很好,但是当我尝试将其转换为foldRight
时,我会得到一个诊断引用MyNonEmpty[T](x, ys)
,其中说:
- type mismatch; found : MyNonEmpty[T] required: MyEmpty[T]
我很感激帮助理解这一点。
abstract class MyList[T] {
def listToMyList1(xs: List[T]): MyList[T] = xs match {
case Nil => MyEmpty[T]
case y :: ys => MyNonEmpty[T](y, listToMyList1(ys))
}
def listToMyList2(xs: List[T]): MyList[T] =
xs.foldRight(MyEmpty[T])((x: T, ys: MyList[T]) => MyNonEmpty[T](x, ys))
case class MyEmpty[T] extends MyList[T]
case class MyNonEmpty[T](val head: T, val tail: MyList[T]) extends MyList[T]
答案 0 :(得分:4)
问题的快速解决方案是,明确定义B
的预期类型。
val empty:MyList[T] = MyEmpty[T]
xs.foldRight(empty)((x, ys) => MyNonEmpty[T](x, ys))
如果你正在深入解决问题,可以描述如下。
foldRight
的签名如下。
def foldLeft[B](z: B)(f: (B, A) ⇒ B): B
此处type B
被定义为方法类型参数。在Scala中,您无法在方法中定义的类型参数中定义类型方差。
def func[+A](a:A) = ... //Compiler error
作为第二点,如果你有多个参数列表,它的行为几乎与curried函数一样。
def func(a:Int)(b:Int) = ... // is almost equal to
def func(a:Int) = (b:Int) => ...
因此,如果您填写第一个arg列表,它将创建另一个函数。在foldRight
案例中,通过填写z
,您也可以定义类型B
。如果没有明确定义z的类型。具体类型设置为type B
。当您尝试给函数f
返回超类型B而不是B
时,编译器会失败。
这是scala类型推断中的一种问题,但除了明确表示类型之外,没有其他可能的解决方案。
答案 1 :(得分:0)
错误是由于foldRight
的定义:
def foldRight[B](z: B)(op: (A, B) ⇒ B): B
z的类型定义了返回类型,在您的情况下为MyEmpty
。我看到两个选择:
sealed trait MyList[T]
case class MyEmpty[T] extends MyList[T]
case class MyNonEmpty[T](val head: T, val tail: MyList[T]) extends MyList[T]
object MyList {
def listToMyList1(xs: List[T]): MyList[T] =
xs.foldRight[MyList[T]](MyEmpty[T])((x: T, ys: MyList[T]) => MyNonEmpty[T](x, ys))
def listToMyList2(xs: List[T]): MyList[T] =
xs.foldRight(MyEmpty[T].asInstanceOf[MyList[T]])((x: T, ys: MyList[T]) => MyNonEmpty[T](x, ys))
}
作为旁注:MyEmpty
可以是单身:case object MyEmpty[Nothing]
,密封的特征协变:`密封特征MyList [+ T]。这样,您只有一个空列表实例,可以使用类型参数T的所有值。