实现TreeMap:为什么我不能打印自己定义的案例类?

时间:2018-03-09 00:37:38

标签: scala treemap case-class

我是Scala的新手。

我尝试使用二进制搜索树实现TreeMap

我发现我可以打印非空TreeMap但代码不会编译,如果 我尝试打印Empty()(这是我定义的案例类)

错误消息是:

  

[error] test.scala:33:16:类型不匹配;

     找到

[错误]:没有<:<没有

     

[错误]要求:K =>序[K]

     

[error] println(Empty())

     

[错误] ^

     

[错误]发现一个错误

     

[error](test:compileIncremental)编译失败

我的源代码:

val tm:TreeMap[Int, String] = Node(Empty(), Empty(), 4, "ddd")
println(tm) // prints "4->ddd"
//println(Empty()) //This won't compile


abstract class TreeMap[K <% Ordered[K], +V] extends AbstractMap[K, V] {

//Implementation omitted.
    override def +[V1 >: V](key: (K, V1)): TreeMap[K, V1] = ......

    override def -(key: K): TreeMap[K, V] = ......

    override def get(key: K): Option[V] = ......

    override def iterator: Iterator[(K, V)] = ......
}

case class Empty[K <% Ordered[K]]() extends TreeMap[K, Nothing]
case class Node[K <% Ordered[K], +V](left: TreeMap[K, V], 
     right: TreeMap[K, V], key: K, value: V) extends TreeMap[K, V]

2 个答案:

答案 0 :(得分:2)

有趣。

很明显,Empty()调用的类型解析存在问题。您可能知道,[A <% Ordered[A]]等同于[A](implicit ev: A => Ordered[A])(并且视图边界已被弃用)。所以我重写了一些不同的方法(部分实现了缺少的方法):

import scala.collection.AbstractMap

abstract class TreeMap[K, +V](implicit ev: K => Ordered[K])
  extends AbstractMap[K, V] {
  //Implementation omitted.
  override def +[V1 >: V](key: (K, V1)): TreeMap[K, V1] = this
  override def -(key: K): TreeMap[K, V] = this
  override def get(key: K): Option[V]
  override def iterator: Iterator[(K, V)]
}


case class Node[K, +V](left: TreeMap[K, V],
                       right: TreeMap[K, V],
                       key: K,
                       value: V)(implicit ev: K => Ordered[K]) 
  extends TreeMap[K, V] {
  override def get(key: K): Option[V] =
    if (key == this.key) Some(value)
    else None

  override def iterator: Iterator[(K, V)] =
    Iterator((key, value))
}

case class Empty[K](implicit ev: K => Ordered[K])
  extends TreeMap[K, Nothing] {
  override def get(key: K): Option[Nothing] = None
  override def iterator: Iterator[(K, Nothing)] = Iterator.empty
}

val tm: TreeMap[Int, String] =
  Node(Empty(), Empty(), 4, "ddd")
val tm2: TreeMap[Int, String] = Empty()

println(tm) // prints Map("4->ddd")
println(tm2) // prints Map()
println(Empty[Nothing]()) //prints Map()
//println(Empty()) //This won't compile

正如您所见,即使致电Empty[Nothing]()也可以。我希望Empty()应该以类似的方式工作,但不是。为了理解原因,我使用了scalac个选项-Xprint:typer -Ydebug -Xprint-types -Ytyper-debug来查看代码的差异:

object SomeClass extends App {
  case class Something3[T](implicit ev: T => Ordered[T])

  Something3()
}

object SomeClass extends App {
  case class Something3[T](implicit ev: T => Ordered[T])

  Something3[Nothing]()
}

在解析类型时看起来像编译器首先尝试解析隐式参数(T => Ordered[T]而不是Nothing => Ordered[Nothing]并得到Nothing => Nothing),而不是类型T,之后你看到错误(这是我对比较输出的理解,没有编译器术语的深入了解)。

我检查了scala.collections.immutable.TreeMap的实现,并且他们在代理商的指导下实现了它。

我可以看到,如果您想将Key作为父级,则无法避免对Empty使用AbstractMap类型。并且也不可能改变K的方差。我想,只要它是编译时错误,你可以保持原样(如果它是你的实现中唯一的问题),注释中的注释应该为Empty显式键入Key {1}}类(有意义的类或Nothing)。

答案 1 :(得分:1)

问题是你添加了一个类型参数和一个绑定到Empty的视图,但你没有提供一个兼容的类型(也不能推断出来)

println(Empty())

可以在上一行推断出兼容类型:

 Node(Empty(), Empty(), 4, "ddd")

我认为正确的解决方法是将Empty()的密钥类型固定为Nothing,并使密钥类型中的TreeMap协变,即

abstract class TreeMap[+K <% Ordered[K], +V] ...

case class Empty() extends TreeMap[Nothing, Nothing]

您还可以使Empty成为案例对象,而不是类,因为它是常量:

case object Empty extends TreeMap[Nothing, Nothing]