如何分组迭代器而不将其转换为scala中的列表?

时间:2015-04-03 06:07:10

标签: scala functional-programming

假设我想在迭代器上groupBy,编译器要求“value groupBy is not a member of Iterator[Int]”。一种方法是将迭代器转换为我想要避免的列表。我想执行groupBy,输入为Iterator[A],输出为Map[B, Iterator[A]]。这样迭代器的一部分只在访问该部分元素时加载,而不是将整个列表加载到内存中。我也知道可能的密钥集,所以我可以说一个特定的密钥是否存在。

def groupBy(iter: Iterator[A], f: fun(A)->B): Map[B, Iterator[A]] = {
    .........
}

3 个答案:

答案 0 :(得分:0)

如果不将结果存储在内存中,我认为这是不可行的(在这种情况下,切换到列表会容易得多)。 Iterator意味着您只能对整个集合进行一次传递。

例如,假设您有一个序列1 2 3 4 5 6,并且您希望groupBy奇数为偶数:

groupBy(it, v => v % 2 == 0)

然后,您可以使用truefalse查询结果以获取迭代器。问题是你应该循环这两个迭代器中的一个直到最后你不能为另一个迭代器做同样的事情(因为你不能在Scala中重置迭代器)。

如果根据您在groupBy中使用的相同规则对元素进行排序,这将是可行的。

答案 1 :(得分:0)

一种可能性是,您可以将Iterator转换为view,然后将groupBy转换为

iter.toTraversable.view.groupBy(_.whatever)

答案 2 :(得分:0)

正如其他回复中所述,在Iterator上实现 lazy groupBy的唯一方法是在内部缓冲元素。内存的最坏情况将在O(n)。如果事先知道密钥在迭代器中分布良好,那么缓冲区就是一个可行的解决方案。

解决方案相对复杂,但Scala源代码中Iterator特征的一些方法是一个良好的开端:

  • 使用buffered方法将头值保留在内存中的partition方法,以及每个生成的迭代器的两个内部队列(前瞻)。
  • span方法也使用buffered方法,这次是前导迭代器的唯一队列。
  • duplicate方法。也许不那么有趣,但我们可以再次观察另一个使用队列来存储两个生成的迭代器之间的差距。

在groupBy的情况下,我们将在上面的示例中使用可变数量的生成迭代器而不是两个。如果需要,我可以尝试编写这种方法。

请注意,您必须事先知道密钥列表。否则,您将需要遍历(和缓冲)整个迭代器以收集构建Map的不同键。