【问题标题】:Interleaving iterators交错迭代器
【发布时间】:2017-01-01 04:32:46
【问题描述】:

我编写了以下代码,期望最后一个 print 方法显示两个迭代器的元素组合。相反,它只显示perfectSquares 的元素。谁能给我解释一下?

object Fuge {

  def main(args: Array[String]) : Unit = {

    perfectSquares.takeWhile(_ < 100).foreach(square => print(square + " "))
    println()
    triangles.takeWhile(_ < 100).foreach(triangle => print(triangle + " "))
    println()
    (perfectSquares++triangles).takeWhile(_ < 100).foreach(combine => print(combine + " "))

  }

  def perfectSquares : Iterator[Int] = {
    Iterator.from(1).map(x => x * x)
  }

  def triangles : Iterator[Int] = {
    Iterator.from(1).map(n => (n * (n + 1)/2))
  }

}

输出

1 4 9 16 25 36 49 64 81    
1 3 6 10 15 21 28 36 45 55 66 78 91 
1 4 9 16 25 36 49 64 81 

【问题讨论】:

  • takeWhile 停止,当它遇到不满足谓词的东西时。

标签: scala iterator


【解决方案1】:

这里使用 Streams 的问题是它们缓存了所有以前的数据。我宁愿按原样交错迭代器,而不涉及流。 像这样的:

class InterleavingIterator[X, X1 <: X, X2 <: X](
    iterator1: Iterator[X1],
    iterator2: Iterator[X2]) extends Iterator[X] {
  private var i2: (Iterator[X], Iterator[X]) = (iterator1, iterator2)

  def hasNext: Boolean = iterator1.hasNext || iterator2.hasNext

  def next: X = {
    i2 = i2.swap
    if (i2._1.hasNext) i2._1.next else i2._2.next
  }
}

【讨论】:

  • 不完全准确。 Stream 只有在有东西挂在头上时才会“缓存”(记忆)。如果Stream 被声明为def,并且没有任何东西挂在头上,那么每个元素在使用后都准备好进行GC。
【解决方案2】:

来自takeWhile上的文档:

  /** Takes longest prefix of values produced by this iterator that satisfy a predicate.
   *
   *  @param   p  The predicate used to test elements.
   *  @return  An iterator returning the values produced by this iterator, until
   *           this iterator produces a value that does not satisfy
   *           the predicate `p`.
   *  @note    Reuse: $consumesAndProducesIterator
   */

这意味着迭代器在那个时刻停止。您创建的是一个迭代器,它远远超过100,然后在某个时候再次从1 开始。但是takeWhile 不会走那么远,因为它已经遇到了高于 100 的数字。请参阅:

object Fuge {
  def main(args: Array[String]) : Unit = {

    perfectSquares.takeWhile(_ < 100).foreach(square => print(square + " "))
    println()
    triangles.takeWhile(_ < 100).foreach(triangle => print(triangle + " "))
    println()
    def interleave (a: Iterator[Int], b: Iterator[Int]): Stream[Int] = {
      if (a.isEmpty || b.isEmpty) { Stream.empty }
      else {
        a.next() #:: b.next() #:: interleave(a, b)
      }
    }
    lazy val interleaved = interleave(perfectSquares, triangles)
    interleaved.takeWhile(_ < 100).foreach(combine => print(combine + " "))
  }

  def perfectSquares : Iterator[Int] = {
    Iterator.from(1).map(x => x * x)
  }

  def triangles : Iterator[Int] = {
    Iterator.from(1).map(n => (n * (n + 1)/2))
  }
}

这里我使用流来懒惰地评估整数序列。这样我们就可以保证交错。请注意,这只是交错的,而不是排序的。

这会产生:

1 4 9 16 25 36 49 64 81 
1 3 6 10 15 21 28 36 45 55 66 78 91 
1 1 4 3 9 6 16 10 25 15 36 21 49 28 64 36 81 45

要在流期间进行排序,您需要 BufferedIterator 并稍微更改 interleave 函数。这是因为调用 next() 会推进迭代器 - 您无法返回。在您需要列表b 中的项目之前,您无法知道您需要列表a 中的多少项目,反之亦然。但是BufferedIterator 允许您调用head,这是一个“窥视”并且不会推进迭代器。现在代码变成了:

object Fuge {
  def main(args: Array[String]) : Unit = {
    perfectSquares.takeWhile(_ < 100).foreach(square => print(square + " "))
    println()
    triangles.takeWhile(_ < 100).foreach(triangle => print(triangle + " "))
    println()
    def interleave (a: BufferedIterator[Int], b: BufferedIterator[Int]): Stream[Int] = {
      if (a.isEmpty || b.isEmpty) { Stream.empty }
      else if (a.head <= b.head){
        a.next() #:: interleave(a, b)
      } else {
        b.next() #:: interleave(a, b)
      }
    }
    lazy val interleaved = interleave(perfectSquares.buffered, triangles.buffered)
    interleaved.takeWhile(_ < 100).foreach(combine => print(combine + " "))
  }

  def perfectSquares : Iterator[Int] = {
    Iterator.from(1).map(x => x * x)
  }

  def triangles : Iterator[Int] = {
    Iterator.from(1).map(n => (n * (n + 1)/2))
  }
}

输出是:

1 4 9 16 25 36 49 64 81 
1 3 6 10 15 21 28 36 45 55 66 78 91 
1 1 3 4 6 9 10 15 16 21 25 28 36 36 45 49 55 64 66 78 81 91

【讨论】:

  • 感谢您的澄清。你如何交错迭代器中的值,这样就不需要单独的 takeWhile 了?我希望迭代器中的值是:1、1、3、4、6、9——我想我会排序?
  • 如果您希望输出交错和排序,则对两个集合进行排序并合并。类似于 Merge-Sort 的“合并”例程。
  • @Jegan 这种方法的困难在于你已经转储了整个迭代器 - 这可能是无限的,就像在这种情况下一样。或者,您转储所需的元素数量(应该已经排序)并 then 合并。但这比懒惰地评估流效率低。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-12-24
  • 2015-12-19
  • 1970-01-01
  • 2012-02-18
  • 1970-01-01
  • 2022-07-04
  • 1970-01-01
相关资源
最近更新 更多