Iterator.duplicateの動き

直前のエントリで気になったのでIterator.duplicateの動きを調べてみた。

val it = Iterator.continually...
val (it1, it2) = it.duplicate

println(it.foldLeft(0)((c, _) => c+1))
println(it1.foldLeft(0)((c, _) => c+1))
println(it2.foldLeft(0)((c, _) => c+1))


これだと、最初の一回目は行数が出てくるが、二回目、三回目では出てこない。duplicateとはいいつつ、元のIteratorを参照しているのだろうか。


そこでこうしてみる。

val it = Iterator.continually...
val (it1, it2) = it.duplicate

println(it1.foldLeft(0)((c, _) => c+1))
println(it.foldLeft(0)((c, _) => c+1))
println(it2.foldLeft(0)((c, _) => c+1))

こうすると、1回目、3回目で行数が出てくる。


最初の試行と矛盾するような動きだが、こうなっているからこうなっているんだ。Iterator.duplicateのインプリを取り急ぎここに貼り付けておこう。やる気になったら見てみよう。
(githubのここ--> scala/Iterator.scala at v2.11.6 · scala/scala · GitHub )

  def duplicate: (Iterator[A], Iterator[A]) = {
    val gap = new scala.collection.mutable.Queue[A]
    var ahead: Iterator[A] = null
    class Partner extends AbstractIterator[A] {
      def hasNext: Boolean = self.synchronized {
        (this ne ahead) && !gap.isEmpty || self.hasNext
      }
      def next(): A = self.synchronized {
        if (gap.isEmpty) ahead = this
        if (this eq ahead) {
          val e = self.next()
          gap enqueue e
          e
        } else gap.dequeue()
      }
      // to verify partnerhood we use reference equality on gap because
      // type testing does not discriminate based on origin.
      private def compareGap(queue: scala.collection.mutable.Queue[A]) = gap eq queue
      override def hashCode = gap.hashCode()
      override def equals(other: Any) = other match {
        case x: Partner   => x.compareGap(gap) && gap.isEmpty
        case _            => super.equals(other)
      }
    }
    (new Partner, new Partner)
  }