首页 文章

语法:: [T] = _在Scala中意味着什么?

提问于
浏览
0

我试图了解Listbuffer是如何实现的 .

@SerialVersionUID(3419063961353022662L)
final class ListBuffer[A]
      extends AbstractBuffer[A]
         with Buffer[A]
         with GenericTraversableTemplate[A, ListBuffer]
         with BufferLike[A, ListBuffer[A]]
         with ReusableBuilder[A, List[A]]
         with SeqForwarder[A]
         with Serializable
{
  override def companion: GenericCompanion[ListBuffer] = ListBuffer

  import scala.collection.Traversable
  import scala.collection.immutable.ListSerializeEnd

  /** Expected invariants:
   *  If start.isEmpty, last0 == null
   *  If start.nonEmpty, last0 != null
   *  If len == 0, start.isEmpty
   *  If len > 0, start.nonEmpty
   */
  private var start: List[A] = Nil
  private var last0: ::[A] = _
  private var exported: Boolean = false
  private var len = 0
  ...
}

第一个问题是,我无法理解私有var last0的语法 ::[A] = _ . 它看起来像一种类型,但它是什么意思:: [A]以及为什么它需要在RHS上?似乎语法用于表示List的最后一个元素,但它是如何工作的?

第二个问题与ListBuffer利用 Var 尾部而不是 Val 尾部(即使它是不可变的)使得加法快速的事实有关 . 以上=函数如何帮助它实现更快的添加?

def += (x: A): this.type = {
  if (exported) copy()
  if (isEmpty) {
    last0 = new :: (x, Nil)
    start = last0
  } else {
    val last1 = last0
    last0 = new :: (x, Nil)
    last1.tl = last0
  }
  len += 1
 this
}

最后一个问题是关于ListBuffer的添加如何工作 . 似乎在空的ListBuffer填充第一个元素之后,start始终指向last0,并且start再次变为空时才开始变化 . 并且在第一个之后添加,将列表的尾部更改为由下面的代码行创建的新列表 .

val last1 = last0 
last0 = new :: (x, Nil)
last1.tl = last0

它看起来像原始列表last0的尾部被更改为包含x的新last0,但似乎这段代码并没有真正扩展start指向的List . 它是如何工作的..?似乎将last0分配给last1.tl会将List(x)附加到start指向的List,但我无法理解它是如何工作的 .

如果可能的话,你能解释一下start,last0,last1将如何随输入序列= 1,= 2,= 3等而改变?

1 回答

  • 3

    :: 可以是methodclass . 在该示例中,它引用参数化类,它将新元素添加到 List 的开头 .

    scala> var x: ::[Int] = _
    x: ::[Int] = null
    
    scala> x = new ::[Int](1, List(2, 3))
    x: ::[Int] = List(1, 2, 3)
    

    虽然 List 是不可变的,但 var x 是可变的( ListBuffer 是可变的) . 它以空值开始,然后设置为新列表 .

    最后, += 方法将一个元素附加到列表和 +=: prepends,并且两者都是常量时间操作(与 List 不同,它具有预先设置的常量时间和O(n ** 2)用于追加 .

    如果你看一下 :: 的定义,你会看到传递给构造函数的 tl 是私有 var .

    final case class ::[B](private var hd: B, private[scala] var tl: List[B]) extends List[B] {
      override def head : B = hd
      override def tail : List[B] = tl
      override def isEmpty: Boolean = false
    

    这意味着它只能在包 scala (它所属的地方)内修改 . 并且 ListBuffer 属于子包 scala.collection.mutable ,因此可以访问 tl . 这是您在 last1.tl 行中的 else 块上看到的内容 .

    所以,关于附加一个新元素:

    我们说 x = 42

    // start is empty:
    last0 = new ::(42, Nil) // List(42)
    start = List(42)
    

    现在,我们要添加追加 43

    // start is not empty
    val last1 = List(42) // last1 is the same object as last0
    last0 = new ::(43, Nil) // temporarily set last0 to List(43) //
    last1.tl = List(43) // as last1 is the same as last0
                        // last0.tl = List(43)
                        // last0 points to the new last element
                        // start holds all elements in the buffer
    

    "Programming in Scala"(the first edition is free online)的第22章解释了如何构造 List 以及如何使用 ListBuffer .

相关问题