Scala:附加线程安全的可变懒惰迭代器

时间:2014-02-14 12:28:39

标签: scala

对于不可变的味道,Iterator完成了这项工作。

val x = Iterator.fill(100000)(someFn)

现在我想实现一个Iterator的可变版本,有三个保证:

  • 所有转换的线程安全(foldfoldLeft,..)和append
  • 懒惰评估
  • 只穿过一次!使用后,应该销毁此Iterator中的对象。

是否有现有的实施方案可以给我这些保证?任何库或框架示例都会很棒。

更新

说明所需的行为。

class SomeThing {}
class Test(val list: Iterator[SomeThing]) {
   def add(thing: SomeThing): Test = {
      new Test(list ++ Iterator(thing))
   }
}
(new Test()).add(new SomeThing).add(new SomeThing);

在这个例子中,SomeThing是一个昂贵的构造,它需要是懒惰的。

永远不需要重复listIterator非常合适。

这应该是异步和懒惰地排序1000万SomeThing个实例,而不会耗尽执行程序(缓存的线程池执行程序)或内存不足。

3 个答案:

答案 0 :(得分:4)

你不需要一个可变的迭代器,只是菊花链不可变的形式:

class SomeThing {}

case class Test(val list: Iterator[SomeThing]) {
  def add(thing: => SomeThing) = Test(list ++ Iterator(thing))
}

(new Test()).add(new SomeThing).add(new SomeThing)

虽然你真的不需要Test这里的额外样板:

Iterator(new SomeThing) ++ Iterator(new SomeThing)

请注意Iterator.++采用副名称参数,因此++操作已经很懒惰。

您可能还想尝试这样做,以避免构建中间迭代器:

Iterator.continually(new SomeThing) take 2

<强>更新

如果您事先不知道尺寸,那么我会经常使用这样的策略:

def mkSomething = if(cond) Some(new Something) else None
Iterator.continually(mkSomething) takeWhile (_.isDefined) map { _.get }

诀窍是让你的生成器函数将其输出包装在一个Option中,然后通过返回None

为你提供一种标记迭代完成的方法

当然......如果你真的推出船,你甚至可以使用可怕的null

def mkSomething = if(cond) { new Something } else null
Iterator.continually(mkSomething) takeWhile (_ != null)

答案 1 :(得分:2)

似乎你需要隐藏迭代器是可变的这一事实,但同时允许它可变地增长。我要提出的建议是我过去加速:::的同样技巧:

 abstract class AppendableIterator[A] extends Iterator[A]{
   protected var inner: Iterator[A]
   def hasNext = inner.hasNext
   def next() = inner next ()

   def append(that: Iterator[A]) = synchronized{
     inner = new JoinedIterator(inner, that)
   }
 }

 //You might need to add some more things, this is a skeleton
 class JoinedIterator[A](first: Iterator[A], second: Iterator[A]) extends Iterator[A]{
   def hasNext = first.hasNext || second.hasNext
   def next() = if(first.hasNext) first next () else if(second.hasNext) second next () else Iterator.next()
 }

所以你真正在做的是将Iterator留在迭代中你可能拥有它的任何地方,同时通过“加入”另一个Iterator来保持追加的线程安全性破坏性。你可以避免重新计算这两者,因为你从未真正强制它们通过CanBuildFrom

这也是仅添加一个项目的概括。如果你愿意的话,你总是可以在A个元素中包含一些Iterator[A]

答案 2 :(得分:0)

您是否查看了collection.parallel包中的mutable.ParIterable

要访问元素上的迭代器,您可以执行类似

的操作
val x = ParIterable.fill(100000)(someFn).iterator

来自文档: Parallel operations are implemented with divide and conquer style algorithms that parallelize well. The basic idea is to split the collection into smaller parts until they are small enough to be operated on sequentially.

...

The higher-order functions passed to certain operations may contain side-effects. Since implementations of bulk operations may not be sequential, this means that side-effects may not be predictable and may produce data-races, deadlocks or invalidation of state if care is not taken. It is up to the programmer to either avoid using side-effects or to use some form of synchronization when accessing mutable data.