Scala 什么更新PrefixMap的继承映射?

Scala 什么更新PrefixMap的继承映射?,scala,scala-collections,Scala,Scala Collections,运行《Scala编程》(第3版)一书中的PrefixMap示例,以及Scala集合的体系结构一章中的PrefixMap示例,我不明白调用update时是什么更新了PrefixMap的继承映射。 代码如下: import collection._ class PrefixMap[T] extends mutable.Map[String, T] with mutable.MapLike[String, T, PrefixMap[T]] { val id: Long = Pref

运行《Scala编程》(第3版)一书中的PrefixMap示例,以及Scala集合的体系结构一章中的PrefixMap示例,我不明白调用update时是什么更新了PrefixMap的继承映射。 代码如下:

import collection._

class PrefixMap[T]
  extends mutable.Map[String, T]
    with mutable.MapLike[String, T, PrefixMap[T]] {

  val id: Long = PrefixMap.nextId
  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 _ =>
      }
      val ret = suffixes(leading) withPrefix (s substring 1)
      println("withPrefix: ends with: id="+this.id+", size="+this.size+", this="+this)
      ret
    }

  override def update(s: String, elem: T) = {
    println("update: this before withPrefix: id="+this.id+", size="+this.size+", return="+this)
    val pm = withPrefix(s)
    println("update: withPrefix returned to update: id="+pm.id+", size="+pm.size+", return="+pm)
    println("===> update: this after withPrefix and before assignment to pm.value : id="+this.id+", size="+this.size+", return="+this)
    pm.value = Some(elem)
    println("===> update: this after assinment to pm.value: id="+this.id+", size="+this.size+", return="+this)
  }

  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]
}

object PrefixMap {
  var ids: Long = 0
  def nextId: Long = { PrefixMap.ids+=1; ids }
}

object MyApp extends App {
  val pm = new PrefixMap[Int]
  pm.update("a", 0)
  println(pm)

}
导入集合_
类前缀映射[T]
扩展mutable.Map[String,T]
使用mutable.MapLike[String,T,PrefixMap[T]]{
valid:Long=PrefixMap.nextId
变量后缀:immutable.Map[Char,PrefixMap[T]]=Map.empty
var值:选项[T]=无
def get(s:字符串):选项[T]=
如果(s.isEmpty)值
else后缀get s(0)flatMap(u.get(s子字符串1))
def with prefix(s:String):PrefixMap[T]=
如果我没有这个
否则{
val前导=s(0)
后缀获得领先匹配{
案例无=>
后缀=后缀+(前导->空)
案例=>
}
val ret=带前缀(s子字符串1)的后缀(前导)
println(“withPrefix:以:id=“+this.id+”,size=“+this.size+”,this=“+this结尾)
ret
}
覆盖def更新(s:String,elem:T)={
println(“更新:this before with prefix:id=“+this.id+”,size=“+this.size+”,return=“+this”)
val pm=带前缀
println(“更新:返回到更新的前缀:id=“+pm.id+”,size=“+pm.size+”,return=“+pm”)
println(“=>update:this-after-withPrefix-to-pm.value:id=“+this.id+”,size=“+this.size+”,return=“+this”)
pm.value=一些(元素)
println(“==>更新:此在协助pm之后。值:id=“+this.id+”,size=“+this.size+”,return=“+this)
}
覆盖def删除(s:字符串):选项[T]=
如果(s.isEmpty){val prev=value;value=None;prev}
else后缀获取s(0)flatMap(u.remove(s子字符串1))
def迭代器:迭代器[(字符串,T)]=
(第0节)

地图(a->0)


因此,问题是:更新方法中带有“pm.value=Some(elem)”的行如何可能导致PrefixMap的继承映射更新为(a->0)?

不清楚您所说的“PrefixMap的继承映射”是什么意思
Map
是一种
trait
,如果您来自Java世界,它类似于
接口
。这意味着
Map
本身没有任何价值,它只是指定契约,并通过“核心”方法提供各种便利方法的一些默认实现(您在
前缀映射中实现的那些)

至于整个数据结构是如何工作的,您应该将这个
PrefixMap
实现想象为一个。从逻辑上讲,每个边都有一个单独的字符(在前缀序列中),每个节点都可能有一个对应于字符串的值,该字符串是通过在从根节点到当前节点的过程中累积所有字符而创建的

因此,如果您有一个带有
“ab”->12
键值的映射,那么树将如下所示:

如果您将
“ac”->123
添加到树中,它将成为

最后,如果将
“a”->1
添加到树中,它将成为:

这里的重要观察结果是,如果将“a”节点作为根,那么剩下的将是一个有效的前缀树,其中所有字符串都由该“a”前缀缩短

实际布局有点不同:

  • 有一个根节点是
    PrefixMap[T]
    ,从外部是
    Map[String,T]
    ,还有一个用于空字符串键的节点
  • 内部节点是
    +
    后缀
    ,即可选值和子节点的合并列表,其边缘上有相应的字符,形成
    映射[Char,PrefixMap[T].
  • 正如您所看到的,
    update
    实现是有效地使用
    withPrefix
    调用并为其赋值。那么
    withPrefix
    方法做什么呢?虽然它是递归实现的,但可能更容易用迭代的方式来考虑它。从这个角度来看,它迭代字符一个接一个地删除给定
    字符串
    ,并在树中导航以创建缺少的节点请参见

    case None=>
    后缀=后缀+(前导->空)
    
    最后返回与整个
    字符串
    对应的节点(即,如果最深的递归
    s.isEmpty
    ,则返回

    方法
    get
    的实现实际上与
    withPrefix
    非常相似:它递归地迭代给定的字符串并在树中导航,但它更简单,因为它不必创建缺少的节点。因为子节点实际上也存储在
    Map
    its
    get
    方法返回
    Option
    同样的方法
    PrefixMap
    应该返回
    Option
    。因此您可以使用
    flatMap
    ,如果在某个级别上没有这样的子节点,它将正常工作

    最后,
    iterator
    将其迭代器创建为

  • value.iterator
    (Scala中的
    选项
    实现了
    iterator
    ,它根据是否有值只返回1或0个元素)
  • 所有子节点的所有
    迭代器
    s只将其自身的字符作为前缀添加到其键中
  • 所以当你这么做的时候

    val pm = new PrefixMap[Int]
    pm.update("a", 0)
    println(pm)
    
    update
    在树中创建are节点并存储值。而
    pm.toString
    实际上使用
    iterate
    来构建字符串表示。因此它在树集合中迭代所有节点中非空的
    value
    选项中的所有值。

    很漂亮,谢谢“PrefixMap的继承映射”我指的是PrefixMap本身就是一个映射,与它引用的映射(后缀)不同。我主要错过的是理解iteraror实现,这就是为什么我对简单的值分配在PrefixMap中生成键值映射感到惊讶。感谢您的清晰解释!