Scala中是否有FIFO流?

时间:2011-09-26 09:42:20

标签: scala stream queue fifo

我在Scala中寻找一个FIFO流,即提供

功能的东西

该流应该是可关闭的,应该阻止访问下一个元素,直到添加了元素或关闭了流。

实际上我有点惊讶的是,集合库没有(似乎)包含这样的数据结构,因为它是IMO非常经典的。

我的问题:

  • 1)我忽略了什么吗?是否已有提供此功能的课程?

  • 2)好的,如果它没有包含在集合库中,那么它可能只是现有集合类的一个简单组合。但是,我试图找到这个简单的代码,但对于这样一个简单的问题,我的实现看起来仍然非常复杂。对于这样的FifoStream,是否有更简单的解决方案?

    class FifoStream[T] extends Closeable {
    
    val queue = new Queue[Option[T]]
    
    lazy val stream = nextStreamElem
    
    private def nextStreamElem: Stream[T] = next() match {
        case Some(elem) => Stream.cons(elem, nextStreamElem)
        case None       => Stream.empty
    }
    
    /** Returns next element in the queue (may wait for it to be inserted). */
    private def next() = {
        queue.synchronized {
            if (queue.isEmpty) queue.wait()
            queue.dequeue()
        }
    }
    
    /** Adds new elements to this stream. */
    def enqueue(elems: T*) {
        queue.synchronized {
            queue.enqueue(elems.map{Some(_)}: _*)
            queue.notify()
        }
    }
    
    /** Closes this stream. */
    def close() {
        queue.synchronized {
            queue.enqueue(None)
            queue.notify()
        }
    }
    }
    

Paradigmatic的解决方案(明显修改)

感谢您的建议。我略微修改了paradigmatic的解决方案,以便toStream返回一个不可变的流(允许可重复读取),以便它符合我的需要。为了完整起见,这里是代码:

import collection.JavaConversions._
import java.util.concurrent.{LinkedBlockingQueue, BlockingQueue}

class FIFOStream[A]( private val queue: BlockingQueue[Option[A]] = new LinkedBlockingQueue[Option[A]]() ) {
  lazy val toStream: Stream[A] = queue2stream
  private def queue2stream: Stream[A] = queue take match {
    case Some(a) => Stream cons ( a, queue2stream )
    case None    => Stream empty
  }
  def close() = queue add None
  def enqueue( as: A* ) = queue addAll as.map( Some(_) )
}

3 个答案:

答案 0 :(得分:15)

在Scala中,流是“功能迭代器”。人们期望它们是纯粹的(没有副作用)和不可改变的。在这种情况下,每次迭代流时都会修改队列(所以它不是纯粹的)。这可能会产生很多误解,因为迭代两次相同的流会产生两种不同的结果。

话虽这么说,你应该使用Java BlockingQueues,而不是滚动你自己的实现。它们被认为在安全性和性能方面得到了很好的实施。这是我能想到的最干净的代码(使用你的方法):

import java.util.concurrent.BlockingQueue
import scala.collection.JavaConversions._

class FIFOStream[A]( private val queue: BlockingQueue[Option[A]] ) {
  def toStream: Stream[A] = queue take match {
    case Some(a) => Stream cons ( a, toStream )
    case None => Stream empty
  }
  def close() = queue add None
  def enqueue( as: A* ) = queue addAll as.map( Some(_) )
}

object FIFOStream {
  def apply[A]() = new LinkedBlockingQueue
}

答案 1 :(得分:2)

我假设您正在寻找类似java.util.concurrent.BlockingQueue的内容?

Akka实现了此接口BoundedBlockingQueue。当然有java.util.concurrent中提供的实现。

你也可以考虑使用Akka的actors来做你正在做的事情。使用Actors通知或推送新事件或消息而不是拉动。

答案 2 :(得分:0)

1)看起来你正在寻找像Oz这样的语言看到的数据流,它支持生产者 - 消费者模式。这样的集合在集合API中不可用,但您可以自己创建一个集合。

2)数据流依赖于single-assignment variables的概念(这样它们不必在声明点初始化并在初始化之前读取它们导致阻塞):

val x: Int
startThread {
  println(x)
}
println("The other thread waits for the x to be assigned")
x = 1

如果语言支持单一赋值(或数据流)变量,那么实现这样的流将是直截了当的(参见link)。由于它们不属于Scala,因此您必须像使用wait - synchronized - notify模式一样使用。

Concurrent queues from Java也可以用来实现,就像其他用户建议的那样。