【问题标题】:How to split up an Iterator?如何拆分迭代器?
【发布时间】:2020-03-27 07:04:38
【问题描述】:

如何将迭代器拆分为带有重复的前缀和其余部分?例如,

def splitDupes(it: Iterator[Int]): (Iterator[Int], Iterator[Int]) = ???

val (xs, ys) = splitDupes(List(1, 1, 1, 2, 3, 4, 5).iterator)
xs.toList // List(1, 1, 1)
ys.toList // List(2, 3, 4, 5)

val (xs, ys) = splitDupes(List(1, 2, 3, 4, 5).iterator)
xs.toList // List(1)
ys.toList // List(2, 3, 4, 5)

val (xs, ys) = splitDupes(List(1, 1, 1, 1, 1).iterator)
xs.toList // List(1, 1, 1, 1, 1)
ys.toList // List()

val (xs, ys) = splitDupes(List[Int]().iterator)
xs.toList // List()
ys.toList // List()

我可以用它来分块读取文本文件吗?

【问题讨论】:

  • 在 List(1, 2, 3, 4, 1, 1) 的情况下会发生什么?
  • (1) 和 (2, 3, 4, 1, 1)
  • 这有点棘手。我假设有一个前提条件,理想情况下,您无需阅读整个文件/列表即可执行此操作。在这种情况下,折叠,分区等是不可能的。我只能想到 (xs, ys) = (...takeWhile(), ....drop(n)) 这将是 O(2n) = O(n) 其中 n 是前缀的长度跨度>
  • 是的,我不想阅读整个文件。无论如何感谢您的建议:)
  • 没问题。如果 takeWhile & drop 方法没有意义,如果你愿意,我可以写一个简单的例子

标签: scala collections iterator


【解决方案1】:

您可以使用span 方法将Iterable 拆分为满足谓词的前缀和不满足谓词的后缀。对于Iterators span 做了正确的事情,并在前缀Iterator 中延迟存储元素,以防在前缀用完之前迭代后缀。

def splitDupes[T](it: Iterator[T]): (Iterator[T], Iterator[T]) = {
  if (it.isEmpty) (Iterator.empty, Iterator.empty)
  else {
    val head = it.next()
    val (dupes, rest) = it.span(_ == head)
    (Iterator(head) ++ dupes, rest)
  }
}

例子:

scala> val (dupes, rest) = splitDupes(Iterator(1,1,1,2,3,2,1))
dupes: Iterator[Int] = <iterator>
rest: Iterator[Int] = <iterator>

scala> (dupes.toList, rest.toList)
res1: (List[Int], List[Int]) = (List(1, 1, 1),List(2, 3, 2, 1))

【讨论】:

  • 非常感谢。这个span 就是我要找的。​​span>
【解决方案2】:

这样的事情怎么样?
(注意:我决定返回一个普通的 List 作为第一部分,因为它已经被消耗掉了)

def splitDupes[A](it: Iterator[A]): (List[A], Iterator[A]) = {
  it.nextOption() match {
    case Some(head) =>
      @annotation.tailrec
      def loop(count: Int): (List[A], Iterator[A]) =
        it.nextOption() match {
          case Some(x) if (x == head) =>
            loop(count + 1)

          case Some(x) =>
            List.fill(count)(head) -> Iterator(Iterator.single(x), it).flatten

          case None =>
            List.fill(count)(head) -> Iterator.empty
        }

      loop(count = 1)

    case None =>
      List.empty -> Iterator.empty
  }
}

【讨论】:

  • 谢谢。喜欢将第一部分返回为List 的想法!
  • @Michael 当然,如果您愿意,您可以在返回之前致电.iterator,但恕我直言,这不会有太大帮助。另外,请注意,我使用了 Scala 2.13,您可以复制 2.12 的代码,您只需将 nextOption() 更改为 if (it.hasNext) it.next()
猜你喜欢
  • 2023-02-13
  • 2020-09-13
  • 1970-01-01
  • 2022-01-21
  • 2020-05-28
  • 2016-01-19
  • 2011-06-02
  • 1970-01-01
  • 2018-12-29
相关资源
最近更新 更多