Scala 为什么使用PrefixMap++;PrefixMap变为可变的.Map
在sbt控制台中运行Scala中编程的PrefixMap示例Scala 为什么使用PrefixMap++;PrefixMap变为可变的.Map,scala,Scala,在sbt控制台中运行Scala中编程的PrefixMap示例 scala> PrefixMap("abc" -> 12, "abb" -> 13) res0: PrefixMap[Int] = Map(abc -> 12, abb -> 13) scala> PrefixMap("aaa" -> 15) res1: PrefixMap[Int] = Map(aaa -> 15) scala> res0 ++ res1 res2: scal
scala> PrefixMap("abc" -> 12, "abb" -> 13)
res0: PrefixMap[Int] = Map(abc -> 12, abb -> 13)
scala> PrefixMap("aaa" -> 15)
res1: PrefixMap[Int] = Map(aaa -> 15)
scala> res0 ++ res1
res2: scala.collection.mutable.Map[String,Int] = Map(abc -> 12, abb -> 13, aaa -> 15)
这个结果把我弄糊涂了
我认为当调用“++”方法时,“++”方法在对象PrefixMap中使用隐式canBuildFrom并创建新的PrefixMap实例,但它似乎在某处使用另一个隐式值并创建新的Map实例
为什么res2类型不是PrefixMap?
或者,我在某个地方犯了个错误
下面是PrefixMap示例中的代码
import collection._
import scala.collection.mutable.{Builder, MapBuilder}
import scala.collection.generic.CanBuildFrom
object PrefixMap {
def empty[T] = new PrefixMap[T]
def apply[T](kvs: (String, T)*): PrefixMap[T] = {
val m: PrefixMap[T] = empty
for (kv <- kvs)
m += kv
m
}
def newBuilder[T]: Builder[(String, T), PrefixMap[T]] =
new MapBuilder[String, T, PrefixMap[T]](empty)
implicit def canBuildFrom[T]
: CanBuildFrom[PrefixMap[_], (String, T), PrefixMap[T]] =
new CanBuildFrom[PrefixMap[_], (String, T), PrefixMap[T]] {
def apply(from: PrefixMap[_]) = newBuilder[T]
def apply() = newBuilder[T]
}
}
class PrefixMap[T] extends mutable.Map[String, T]
with mutable.MapLike[String, T, PrefixMap[T]] {
var suffixes: immutable.Map[Char, PrefixMap[T]] = Map.empty
var value: Option[T] = None
def get(s: String): Option[T] =
if(s.isEmpty) value
else suffixes get (s(0)) flatMap (_.get(s substring 1))
def withPrefix(s: String): PrefixMap[T] = {
if(s.isEmpty) this
else {
val leading = s(0)
suffixes get leading match {
case None => suffixes = suffixes + (leading -> empty)
case _ =>
}
suffixes(leading) withPrefix (s substring 1)
}
}
override def update(s: String, elem: T) =
withPrefix(s).value = Some(elem)
override def remove(s: String): Option[T] =
if(s.isEmpty) { val prev = value; value = None; prev}
else suffixes get (s(0)) flatMap (_.remove(s substring 1))
def iterator: Iterator[(String, T)] =
(for (v <- value.iterator) yield ("", v)) ++
(for ((chr, m) <- suffixes.iterator;
(s, v) <- m.iterator) yield (chr +: s, v))
def += (kv: (String, T)): this.type = { update(kv._1, kv._2); this }
def -= (s: String): this.type = { remove(s); this }
override def empty = new PrefixMap[T]
}
导入集合_
导入scala.collection.mutable.{Builder,MapBuilder}
导入scala.collection.generic.CanBuildFrom
对象前缀映射{
def empty[T]=新的前缀映射[T]
def应用[T](kvs:(字符串,T)*):前缀映射[T]={
val m:PrefixMap[T]=空
对于(kv后缀=后缀+(前导->空)
案例=>
}
带前缀的后缀(前导)(s子字符串1)
}
}
覆盖def更新(s:String,elem:T)=
带前缀。值=一些(元素)
覆盖def删除(s:字符串):选项[T]=
如果(s.isEmpty){val prev=value;value=None;prev}
else后缀get(s(0))flatMap(u.remove(s子字符串1))
def迭代器:迭代器[(字符串,T)]=
(对于(v首先,重要的是要理解变量的类型和存储在该变量中的值的类型之间存在差异。在您的示例中,++
结果的实际类型仍然是PrefixMap
,但变量的类型(即编译器可以证明的)只是mutable.Map
,这是您在REPL中看到的。您可以通过打印res2.getClass
以获取实际类型来轻松验证这一点
我认为这是因为Map
实际上有两种不同的++
方法:
- 一个来自
TraversabelLike
,它是一个智能的,具有CanBuildFrom
和所有其他奇特的功能
- 另一个来自
scala.collection.MapLike
在scala.collection.mutable.MapLike
处被覆盖,它的通用性要小得多
//collection.MapLike所以这里的Map就是collection.Map
def++[V1>:V](xs:gentraversableone[(K,V1)]):Map[K,V1]
//因此,这里的输出映射是mutable.Map
覆盖def++[V1>:V](xs:gentraversableone[(K,V1)]):映射[K,V1]
由于您的代码位于特定的上下文中,编译器确切地知道类(而不是像泛型的.filter
这样必须使用TraversableLike
基础结构),因此编译器使用来自mutable.MapLike
的更简单的++
我不确定为什么会首先存在另一个MapLike.++
方法。这可能是为了支持与某些遗留设计的向后兼容性(Scala collections library已经被重新设计了好几次)。但唯一的方法是创建这组方法(还有几个类似+
)要想按您的意愿工作,就要做mutable.MapLike
所做的事情:即在PrefixMap
中覆盖它们,定义更具体的类型。还要注意MapLike.++
内部使用clone()
,因此您最好正确使用它或重新实现它。PrefixMap
s是mutable.Map
s。它看起来像是将其视为常见的超类型。在阅读了您的文章后,我验证了+
方法是否调用mutable.MapLike.++
。结果肯定满足了+
的要求hod调用mutable.MapLike.++
而不是调用TraversableLike.++
方法,该方法具有隐式CanBuildFrom
参数。谢谢您的回答。